From cf2c69083032f31b3b229e9b2e73be5021ae85cd Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Sat, 10 Jun 2017 11:34:56 +0000 Subject: [PATCH] Bug 18790: Add ability to void payments We've had the ability to 'reverse' a payment for a long time, but it would be much better if we had a true void payment function that replaces the paid amounts into the fee so that it appears as if the payment was never made. Test Plan: 1) Apply this patch and dependent patches 2) Run updatedatabase.pl 3) Create some fines 4) Pay those fines 5) Use the new 'void' button to void the payments 6) Note the fines were restored to their pre-payment amounts Signed-off-by: Koha-us conference Signed-off-by: BWS Sandboxes --- Koha/Account/Line.pm | 46 +++++++++++++++++ installer/data/mysql/account_offset_types.sql | 3 +- .../prog/en/modules/members/boraccount.tt | 2 + members/boraccount.pl | 7 ++- t/db_dependent/Accounts.t | 59 +++++++++++++++++++++- 5 files changed, 114 insertions(+), 3 deletions(-) diff --git a/Koha/Account/Line.pm b/Koha/Account/Line.pm index f7578e7..c060b31 100644 --- a/Koha/Account/Line.pm +++ b/Koha/Account/Line.pm @@ -20,6 +20,7 @@ use Modern::Perl; use Carp; use Koha::Database; +use Koha::Account::Offsets; use base qw(Koha::Object); @@ -33,6 +34,51 @@ Koha::Account::Lines - Koha accountline Object class =cut +=head3 void + +$payment_accountline->void(); + +=cut + +sub void { + my ($self) = @_; + + # Make sure it is a payment we are voiding + return unless $self->accounttype =~ /^Pay/; + + my @account_offsets = + Koha::Account::Offsets->search( { credit_id => $self->id, type => 'Payment' } ); + + foreach my $account_offset (@account_offsets) { + my $fee_paid = Koha::Account::Lines->find( $account_offset->debit_id ); + + next unless $fee_paid; + + my $amount_paid = $account_offset->amount * -1; # amount paid is stored as a negative amount + my $new_amount = $fee_paid->amountoutstanding + $amount_paid; + $fee_paid->amountoutstanding($new_amount); + $fee_paid->store(); + + Koha::Account::Offset->new( + { + credit_id => $self->id, + debit_id => $fee_paid->id, + amount => $amount_paid, + type => 'Void Payment', + } + )->store(); + } + + $self->set( + { + accounttype => 'VOID', + amountoutstanding => 0, + amount => 0, + } + ); + $self->store(); +} + =head3 type =cut diff --git a/installer/data/mysql/account_offset_types.sql b/installer/data/mysql/account_offset_types.sql index a0f2fb1..a9eaadc 100644 --- a/installer/data/mysql/account_offset_types.sql +++ b/installer/data/mysql/account_offset_types.sql @@ -9,4 +9,5 @@ INSERT INTO account_offset_types ( type ) VALUES ('Dropbox'), ('Rental Fee'), ('Fine Update'), -('Fine'); +('Fine'), +('Void Payment'); diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt index d7622fd..ceb503e 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt @@ -60,6 +60,7 @@ [% CASE 'Pay00' %]Payment, thanks (cash via SIP2) [% CASE 'Pay01' %]Payment, thanks (VISA via SIP2) [% CASE 'Pay02' %]Payment, thanks (credit card via SIP2) + [% CASE 'VOID' %]Payment, Voided [% CASE 'N' %]New card [% CASE 'F' %]Fine [% CASE 'A' %]Account management fee @@ -93,6 +94,7 @@ [% IF ( reverse_col) %] [% IF ( account.payment ) %] Reverse + Void [% ELSE %][% SET footerjs = 1 %]   [% END %] diff --git a/members/boraccount.pl b/members/boraccount.pl index af35fe8..e0414cd 100755 --- a/members/boraccount.pl +++ b/members/boraccount.pl @@ -48,7 +48,7 @@ my ($template, $loggedinuser, $cookie) = get_template_and_user( } ); -my $borrowernumber=$input->param('borrowernumber'); +my $borrowernumber = $input->param('borrowernumber'); my $action = $input->param('action') || ''; my $logged_in_user = Koha::Patrons->find( $loggedinuser ) or die "Not logged in"; @@ -63,6 +63,11 @@ output_and_exit_if_error( $input, $cookie, $template, { module => 'members', log if ( $action eq 'reverse' ) { ReversePayment( scalar $input->param('accountlines_id') ); } +elsif ( $action eq 'void' ) { + my $payment_id = scalar $input->param('accountlines_id'); + my $payment = Koha::Account::Lines->find( $payment_id ); + $payment->void(); +} if ( $patron->is_child ) { my $patron_categories = Koha::Patron::Categories->search_limited({ category_type => 'A' }, {order_by => ['categorycode']}); diff --git a/t/db_dependent/Accounts.t b/t/db_dependent/Accounts.t index 60c010d..2ce9474 100644 --- a/t/db_dependent/Accounts.t +++ b/t/db_dependent/Accounts.t @@ -18,7 +18,7 @@ use Modern::Perl; -use Test::More tests => 23; +use Test::More tests => 24; use Test::MockModule; use Test::Warn; @@ -630,4 +630,61 @@ subtest "Koha::Account::chargelostitem tests" => sub { is( $procfee->amount, "2.040000", "Processing fee if processing fee"); }; +subtest "Koha::Account::Line::void tests" => sub { + + plan tests => 12; + + # 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; + + my $account = Koha::Account->new({ patron_id => $borrower->id }); + + my $line1 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amount => 10, amountoutstanding => 10 })->store(); + my $line2 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amount => 20, amountoutstanding => 20 })->store(); + + is( $account->balance(), "30.000000", "Account balance is 30" ); + is( $line1->amountoutstanding, 10, 'First fee has amount outstanding of 10' ); + is( $line2->amountoutstanding, 20, 'Second fee has amount outstanding of 20' ); + + my $id = $account->pay( + { + lines => [$line1, $line2], + amount => 30, + } + ); + my $account_payment = Koha::Account::Lines->find( $id ); + + is( $account->balance(), "0.000000", "Account balance is 0" ); + + $line1->_result->discard_changes(); + $line2->_result->discard_changes(); + is( $line1->amountoutstanding, '0.000000', 'First fee has amount outstanding of 0' ); + is( $line2->amountoutstanding, '0.000000', 'Second fee has amount outstanding of 0' ); + + $account_payment->void(); + + is( $account->balance(), "30.000000", "Account balance is again 30" ); + + $account_payment->_result->discard_changes(); + $line1->_result->discard_changes(); + $line2->_result->discard_changes(); + + is( $account_payment->accounttype, 'VOID', 'Voided payment accounttype is VOID' ); + is( $account_payment->amount, '0.000000', 'Voided payment amount is 0' ); + is( $account_payment->amountoutstanding, '0.000000', 'Voided payment amount outstanding is 0' ); + + is( $line1->amountoutstanding, '10.000000', 'First fee again has amount outstanding of 10' ); + is( $line2->amountoutstanding, '20.000000', 'Second fee again has amount outstanding of 20' ); +}; + 1; -- 2.10.2