@@ -, +, @@ on it. It should now be marked as cancelled that it still works --- Koha/Account/Line.pm | 50 +++++++++++++++- .../intranet-tmpl/prog/en/includes/accounts.inc | 1 + .../prog/en/modules/members/boraccount.tt | 15 ++++- members/boraccount.pl | 6 ++ members/cancel-charge.pl | 48 ++++++++++++++++ t/db_dependent/Koha/Account/Line.t | 66 ++++++++++++++++++++++ 6 files changed, 183 insertions(+), 3 deletions(-) create mode 100755 members/cancel-charge.pl --- a/Koha/Account/Line.pm +++ a/Koha/Account/Line.pm @@ -285,6 +285,54 @@ sub void { } +=head3 cancel + + $debit_accountline->cancel(); + +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 + +=cut + +sub cancel { + my ($self) = @_; + + # Make sure it is a charge we are cancelling + return unless $self->is_debit; + + # Make sure it is not already cancelled + return if $self->status && $self->status eq '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, + })); + } + + $self->set({ + status => 'CANCELLED', + amountoutstanding => 0, + amount => 0, + }); + $self->store(); + + return $self; +} + =head3 reduce $charge_accountline->reduce({ @@ -755,7 +803,7 @@ sub adjust { sub is_credit { my ($self) = @_; - return ( $self->amount < 0 ); + return defined $self->credit_type_code; } =head3 is_debit --- a/koha-tmpl/intranet-tmpl/prog/en/includes/accounts.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/accounts.inc @@ -55,6 +55,7 @@ [%- CASE 'FORGIVEN' -%] (Forgiven) [%- CASE 'VOID' -%] (Voided) [%- CASE 'LOST' -%] (Lost) + [%- CASE 'CANCELLED' -%] (Cancelled) [%- CASE -%] [%- END -%] [%- END -%] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt @@ -89,8 +89,19 @@ [% IF account.is_debit && account.amountoutstanding > 0 %] Pay [% END %] - [% IF account.is_credit %] - Void + [% IF account.is_credit && account.status != 'VOID' %] + Void payment + [% END %] + [% IF account.is_debit && account.amount == account.amountoutstanding && account.status != 'CANCELLED' %] +
+ + + + +
[% END %] [% IF CAN_user_updatecharges_payout && account.is_credit && ( account.amountoutstanding < 0 ) %] --- a/members/boraccount.pl +++ a/members/boraccount.pl @@ -34,6 +34,7 @@ use Koha::Cash::Registers; use Koha::Patrons; use Koha::Patron::Categories; use Koha::Items; +use Koha::Token; my $input=new CGI; @@ -202,6 +203,10 @@ foreach my $renew_result(@renew_results) { }; } +my $csrf_token = Koha::Token->new->generate_csrf({ + session_id => scalar $input->cookie('CGISESSID'), +}); + $template->param( patron => $patron, finesview => 1, @@ -211,6 +216,7 @@ $template->param( payment_id => $payment_id, change_given => $change_given, renew_results => $renew_results_display, + csrf_token => $csrf_token, ); output_html_with_http_headers $input, $cookie, $template->output; --- a/members/cancel-charge.pl +++ a/members/cancel-charge.pl @@ -0,0 +1,48 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use CGI; + +use C4::Auth; +use Koha::Token; + +my $cgi = CGI->new; + +my $authnotrequired = 0; +my $flags = { updatecharges => 'remaining_permissions' }; +my $type = 'intranet'; +my ($user, $cookie) = C4::Auth::checkauth($cgi, $authnotrequired, $flags, $type); + +my $csrf_token_is_valid = Koha::Token->new->check_csrf( { + session_id => scalar $cgi->cookie('CGISESSID'), + token => scalar $cgi->param('csrf_token'), +}); +unless ($csrf_token_is_valid) { + print $cgi->header('text/plain', '403 Forbidden'); + print 'Wrong CSRF token'; + exit; +} + +my $borrowernumber = $cgi->param('borrowernumber'); +my $accountlines_id = $cgi->param('accountlines_id'); + +my $line = Koha::Account::Lines->find($accountlines_id); +$line->cancel(); + +print $cgi->redirect('/cgi-bin/koha/members/boraccount.pl?borrowernumber=' . $borrowernumber); --- a/t/db_dependent/Koha/Account/Line.t +++ a/t/db_dependent/Koha/Account/Line.t @@ -1076,4 +1076,70 @@ subtest "reduce() tests" => sub { $schema->storage->txn_rollback; }; +subtest "cancel() tests" => sub { + plan tests => 9; + + $schema->storage->txn_begin; + + # Create a borrower + my $categorycode = $builder->build({ source => 'Category' })->{ categorycode }; + my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; + + my $borrower = Koha::Patron->new( { + cardnumber => 'dariahall', + surname => 'Hall', + firstname => 'Daria', + } ); + $borrower->categorycode( $categorycode ); + $borrower->branchcode( $branchcode ); + $borrower->store; + + t::lib::Mocks::mock_userenv({ branchcode => $branchcode, borrowernumber => $borrower->borrowernumber }); + + my $account = Koha::Account->new({ patron_id => $borrower->id }); + + my $line1 = Koha::Account::Line->new( + { + borrowernumber => $borrower->borrowernumber, + amount => 10, + amountoutstanding => 10, + interface => 'commandline', + debit_type_code => 'OVERDUE', + } + )->store(); + my $line2 = Koha::Account::Line->new( + { + borrowernumber => $borrower->borrowernumber, + amount => 20, + amountoutstanding => 20, + interface => 'commandline', + debit_type_code => 'OVERDUE', + } + )->store(); + + my $id = $account->pay({ + lines => [$line2], + amount => 5, + }); + + is( $account->balance(), 25, "Account balance is 25" ); + is( $line1->amountoutstanding+0, 10, 'First fee has amount outstanding of 10' ); + is( $line2->amountoutstanding+0, 15, 'Second fee has amount outstanding of 15' ); + + my $ret = $line1->cancel(); + is( ref($ret), 'Koha::Account::Line', 'Cancel returns the account line' ); + is( $account->balance(), 15, "Account balance is 15" ); + is( $line1->amount+0, 0, 'First fee has amount of 0' ); + is( $line1->amountoutstanding+0, 0, 'First fee has amount outstanding of 0' ); + + $ret = $line2->cancel(); + is ($ret, undef, 'cancel returns undef when line cannot be cancelled'); + + my $account_payment = Koha::Account::Lines->find($id); + $ret = $account_payment->cancel(); + is ($ret, undef, 'payment cannot be cancelled'); + + $schema->storage->txn_rollback; +}; + 1; --