X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=Koha%2FAccount%2FLine.pm;h=a05ab65c282cd02f5e0e936e9081f071a319709b;hb=dec9f754f8bbebaf6d2a2e9ba1ce1b9935ba45da;hp=9f6723f6e5dad18f2e9f475558b299e37595d53a;hpb=8d2259b67443f75675733d41773d153d889dffd7;p=koha-ffzg.git diff --git a/Koha/Account/Line.pm b/Koha/Account/Line.pm index 9f6723f6e5..a05ab65c28 100644 --- a/Koha/Account/Line.pm +++ b/Koha/Account/Line.pm @@ -17,17 +17,16 @@ package Koha::Account::Line; use Modern::Perl; -use Carp; -use Data::Dumper; +use Data::Dumper qw( Dumper ); -use C4::Log qw(logaction); -use C4::Overdues qw(GetFine); +use C4::Log qw( logaction ); +use C4::Overdues qw( UpdateFine ); use Koha::Account::CreditType; use Koha::Account::DebitType; use Koha::Account::Offsets; use Koha::Database; -use Koha::DateUtils; +use Koha::DateUtils qw( dt_from_string ); use Koha::Exceptions::Account; use Koha::Items; @@ -132,8 +131,8 @@ Return the credit_offsets linked to this account line if some exist =cut sub credit_offsets { - my ( $self ) = @_; - my $rs = $self->_result->account_offsets_credits; + my ( $self, $cond, $attr ) = @_; + my $rs = $self->_result->search_related( 'account_offsets_credits', $cond, $attr); return unless $rs; return Koha::Account::Offsets->_new_from_dbic($rs); } @@ -145,8 +144,8 @@ Return the debit_offsets linked to this account line if some exist =cut sub debit_offsets { - my ( $self ) = @_; - my $rs = $self->_result->account_offsets_debits; + my ( $self, $cond, $attr ) = @_; + my $rs = $self->_result->search_related( 'account_offsets_debits', $cond, $attr); return unless $rs; return Koha::Account::Offsets->_new_from_dbic($rs); } @@ -210,7 +209,10 @@ sub debits { =head3 void - $payment_accountline->void(); + $payment_accountline->void({ + interface => $interface, + [ staff_id => $staff_id, branch => $branchcode ] + }); Used to 'void' (or reverse) a payment/credit. It will roll back any offsets created by the application of this credit upon any debits and mark the credit @@ -219,17 +221,81 @@ as 'void' by updating it's status to "VOID". =cut sub void { - my ($self) = @_; + my ($self, $params) = @_; + + # Make sure it is a credit we are voiding + unless ( $self->is_credit ) { + Koha::Exceptions::Account::IsNotCredit->throw( + error => 'Account line ' . $self->id . 'is not a credit' ); + } + + # Make sure it is not already voided + if ( $self->status && $self->status eq 'VOID' ) { + Koha::Exceptions::Account->throw( + error => 'Account line ' . $self->id . 'is already void' ); + } - # Make sure it is a payment we are voiding - return unless $self->amount < 0; + # Check for mandatory parameters + my @mandatory = ( 'interface' ); + for my $param (@mandatory) { + unless ( defined( $params->{$param} ) ) { + Koha::Exceptions::MissingParameter->throw( + error => "The $param parameter is mandatory" ); + } + } + + # More mandatory parameters + if ( $params->{interface} eq 'intranet' ) { + my @optional = ( 'staff_id', 'branch' ); + for my $param (@optional) { + unless ( defined( $params->{$param} ) ) { + Koha::Exceptions::MissingParameter->throw( error => +"The $param parameter is mandatory when interface is set to 'intranet'" + ); + } + } + } + # Find any applied offsets for the credit so we may reverse them my @account_offsets = Koha::Account::Offsets->search( { credit_id => $self->id, amount => { '<' => 0 } } ); + my $void; $self->_result->result_source->schema->txn_do( sub { + + # A 'void' is a 'debit' + $void = Koha::Account::Line->new( + { + borrowernumber => $self->borrowernumber, + date => \'NOW()', + debit_type_code => 'VOID', + amount => $self->amount * -1, + amountoutstanding => $self->amount * -1, + manager_id => $params->{staff_id}, + interface => $params->{interface}, + branchcode => $params->{branch}, + } + )->store(); + + # Record the creation offset + Koha::Account::Offset->new( + { + debit_id => $void->id, + type => 'CREATE', + amount => $self->amount * -1 + } + )->store(); + + # Link void to payment + $self->set({ + amountoutstanding => $self->amount, + status => 'VOID' + })->store(); + $self->apply( { debits => [$void] } ); + + # Reverse any applied payments foreach my $account_offset (@account_offsets) { my $fee_paid = Koha::Account::Lines->find( $account_offset->debit_id ); @@ -246,7 +312,7 @@ sub void { credit_id => $self->id, debit_id => $fee_paid->id, amount => $amount_paid, - type => 'Void Payment', + type => 'VOID', } )->store(); } @@ -273,18 +339,11 @@ sub void { ) ); } - - $self->set( - { - status => 'VOID', - amountoutstanding => 0, - amount => 0, - } - ); - $self->store(); } ); + $void->discard_changes; + return $void; } =head3 cancel @@ -293,46 +352,85 @@ sub void { Cancel a charge. It will mark the debit as 'cancelled' by updating its status to 'CANCELLED'. + Charges that have been fully or partially paid cannot be cancelled. -Return self in case of success, undef otherwise +Returns the cancellation accountline. =cut sub cancel { - my ($self) = @_; + my ( $self, $params ) = @_; - # Make sure it is a charge we are cancelling - return unless $self->is_debit; + # Make sure it is a charge we are reducing + unless ( $self->is_debit ) { + Koha::Exceptions::Account::IsNotDebit->throw( + error => 'Account line ' . $self->id . 'is not a debit' ); + } + if ( $self->debit_type_code eq 'PAYOUT' ) { + Koha::Exceptions::Account::IsNotDebit->throw( + error => 'Account line ' . $self->id . 'is a payout' ); + } # Make sure it is not already cancelled - return if $self->status && $self->status eq 'CANCELLED'; + if ( $self->status && $self->status eq 'CANCELLED' ) { + Koha::Exceptions::Account->throw( + error => 'Account line ' . $self->id . 'is already cancelled' ); + } # Make sure it has not be paid yet - return if $self->amount != $self->amountoutstanding; - - if ( C4::Context->preference("FinesLog") ) { - logaction('FINES', 'CANCEL', $self->borrowernumber, Dumper({ - action => 'cancel_charge', - borrowernumber => $self->borrowernumber, - amount => $self->amount, - amountoutstanding => $self->amountoutstanding, - description => $self->description, - debit_type_code => $self->debit_type_code, - note => $self->note, - itemnumber => $self->itemnumber, - manager_id => $self->manager_id, - })); + if ( $self->amount != $self->amountoutstanding ) { + Koha::Exceptions::Account->throw( + error => 'Account line ' . $self->id . 'is already offset' ); } - $self->set({ - status => 'CANCELLED', - amountoutstanding => 0, - amount => 0, - }); - $self->store(); + # Check for mandatory parameters + my @mandatory = ( 'staff_id', 'branch' ); + for my $param (@mandatory) { + unless ( defined( $params->{$param} ) ) { + Koha::Exceptions::MissingParameter->throw( + error => "The $param parameter is mandatory" ); + } + } - return $self; + my $cancellation; + $self->_result->result_source->schema->txn_do( + sub { + + # A 'cancellation' is a 'credit' + $cancellation = Koha::Account::Line->new( + { + date => \'NOW()', + amount => 0 - $self->amount, + credit_type_code => 'CANCELLATION', + status => 'ADDED', + amountoutstanding => 0 - $self->amount, + manager_id => $params->{staff_id}, + borrowernumber => $self->borrowernumber, + interface => 'intranet', + branchcode => $params->{branch}, + } + )->store(); + + my $cancellation_offset = Koha::Account::Offset->new( + { + credit_id => $cancellation->accountlines_id, + type => 'CREATE', + amount => 0 - $self->amount + } + )->store(); + + # Link cancellation to charge + $cancellation->apply( { debits => [$self] } ); + $cancellation->status('APPLIED')->store(); + + # Update status of original debit + $self->status('CANCELLED')->store; + } + ); + + $cancellation->discard_changes; + return $cancellation; } =head3 reduce @@ -436,8 +534,8 @@ sub reduce { my $reduction_offset = Koha::Account::Offset->new( { credit_id => $reduction->accountlines_id, - type => uc( $params->{reduction_type} ), - amount => $params->{amount} + type => 'CREATE', + amount => 0 - $params->{amount} } )->store(); @@ -445,22 +543,18 @@ sub reduce { my $debit_outstanding = $self->amountoutstanding; if ( $debit_outstanding >= $params->{amount} ) { - $reduction->apply( - { - debits => [$self], - offset_type => uc( $params->{reduction_type} ) - } - ); + $reduction->apply( { debits => [$self] } ); $reduction->status('APPLIED')->store(); } else { - # Zero amount offset used to link original 'debit' to reduction 'credit' + # Zero amount offset used to link original 'debit' to + # reduction 'credit' my $link_reduction_offset = Koha::Account::Offset->new( { credit_id => $reduction->accountlines_id, debit_id => $self->accountlines_id, - type => uc( $params->{reduction_type} ), + type => 'APPLY', amount => 0 } )->store(); @@ -478,7 +572,7 @@ sub reduce { =head3 apply my $debits = $account->outstanding_debits; - my $outstanding_amount = $credit->apply( { debits => $debits, [ offset_type => $offset_type ] } ); + my $credit = $credit->apply( { debits => $debits } ); Applies the credit to a given debits array reference. @@ -488,9 +582,6 @@ Applies the credit to a given debits array reference. =item debits - Koha::Account::Lines object set of debits -=item offset_type (optional) - a string indicating the offset type (valid values are those from -the 'account_offset_types' table) - =back =cut @@ -499,7 +590,6 @@ sub apply { my ( $self, $params ) = @_; my $debits = $params->{debits}; - my $offset_type = $params->{offset_type} // 'Credit Applied'; unless ( $self->is_credit ) { Koha::Exceptions::Account::IsNotCredit->throw( @@ -540,7 +630,7 @@ sub apply { { credit_id => $self->id, debit_id => $debit->id, amount => $amount_to_cancel * -1, - type => $offset_type, + type => 'APPLY' } )->store(); @@ -551,9 +641,17 @@ sub apply { # Attempt to renew the item associated with this debit if # appropriate - if ($debit->renewable) { - $debit->renew_item($params->{interface}); + if ( $self->credit_type_code ne 'FORGIVEN' && $debit->is_renewable ) { + my $outcome = $debit->renew_item( { interface => $params->{interface} } ); + $self->add_message( + { + type => 'info', + message => 'renewal', + payload => $outcome + } + ) if $outcome; } + $debit->discard_changes; # Refresh values from DB to clear floating point remainders # Same logic exists in Koha::Account::pay if ( @@ -572,10 +670,12 @@ sub apply { C4::Circulation::ReturnLostItem( $self->borrowernumber, $debit->itemnumber ); } + + last if $available_credit == 0; } }); - return $available_credit; + return $self; } =head3 payout @@ -658,12 +758,12 @@ sub payout { my $payout_offset = Koha::Account::Offset->new( { debit_id => $payout->accountlines_id, - type => 'PAYOUT', + type => 'CREATE', amount => $amount } )->store(); - $self->apply( { debits => [$payout], offset_type => 'PAYOUT' } ); + $self->apply( { debits => [$payout] } ); $self->status('PAID')->store; } ); @@ -829,6 +929,7 @@ on the API. sub to_api_mapping { return { accountlines_id => 'account_line_id', + credit_number => undef, credit_type_code => 'credit_type', debit_type_code => 'debit_type', amountoutstanding => 'amount_outstanding', @@ -838,17 +939,18 @@ sub to_api_mapping { itemnumber => 'item_id', manager_id => 'user_id', note => 'internal_note', + register_id => 'cash_register_id', }; } -=head3 renewable +=head3 is_renewable - my $bool = $line->renewable; + my $bool = $line->is_renewable; =cut -sub renewable { +sub is_renewable { my ($self) = @_; return ( @@ -856,7 +958,9 @@ sub renewable { $self->debit_type_code && $self->debit_type_code eq 'OVERDUE' && $self->status && - $self->status eq 'UNRETURNED' + $self->status eq 'UNRETURNED' && + $self->item && + $self->patron ) ? 1 : 0; } @@ -865,7 +969,8 @@ sub renewable { my $renew_result = $line->renew_item; Conditionally attempt to renew an item and return the outcome. This is -as a consequence of the fine on an item being fully paid off +as a consequence of the fine on an item being fully paid off. +Caller must call is_renewable before. =cut @@ -874,19 +979,14 @@ sub renew_item { my $outcome = {}; - # We want to reject the call to renew if any of these apply: + # We want to reject the call to renew if: # - The RenewAccruingItemWhenPaid syspref is off - # - The line item doesn't have an item attached to it - # - The line item doesn't have a patron attached to it - # + # OR # - The RenewAccruingItemInOpac syspref is off - # AND # - There is an interface param passed and it's value is 'opac' if ( !C4::Context->preference('RenewAccruingItemWhenPaid') || - !$self->item || - !$self->patron || ( !C4::Context->preference('RenewAccruingItemInOpac') && $params->{interface} &&