From 9e076003fad105766b46d0b0ae745fb1996cb1cf Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Tue, 10 Jul 2018 14:54:17 -0300 Subject: [PATCH] Bug 20944: Add route to add credits to a patron's account This patch adds the /patrons/{patron_id}/account/credits endpoint, that can be used to add credits to a patron's account. It is implemented so the new credits are used to pay existing debts. To test: - Run: $ kshell k$ prove t/db_dependent/api/v1/patrons_accounts.t => SUCCESS: Tests pass! - Make your favourite REST testing tool (RESTer on Firefox?) do: POST /api/v1/patrons/{patron_id}/account/credits { "amount": 100 } - Play with other possible attributes on the credit object. - Sign off :-D Signed-off-by: Martin Renvoize Signed-off-by: Josef Moravec --- Koha/REST/V1/Patrons/Account.pm | 87 ++++++++++++++++++++++++ t/db_dependent/api/v1/patrons_accounts.t | 110 ++++++++++++++++++++++++++----- 2 files changed, 182 insertions(+), 15 deletions(-) diff --git a/Koha/REST/V1/Patrons/Account.pm b/Koha/REST/V1/Patrons/Account.pm index a431998..7cae3b3 100644 --- a/Koha/REST/V1/Patrons/Account.pm +++ b/Koha/REST/V1/Patrons/Account.pm @@ -19,6 +19,9 @@ use Modern::Perl; use Mojo::Base 'Mojolicious::Controller'; +use Koha::Patrons; + +use Scalar::Util qw(blessed); use Try::Tiny; =head1 NAME @@ -69,6 +72,90 @@ sub get { return $c->render( status => 200, openapi => $balance ); } +=head3 add_credit + +Controller function that handles adding a credit to a patron's account + +=cut + +sub add_credit { + my $c = shift->openapi->valid_input or return; + + my $patron_id = $c->validation->param('patron_id'); + my $patron = Koha::Patrons->find($patron_id); + my $user = $c->stash('koha.user'); + + + unless ($patron) { + return $c->render( status => 404, openapi => { error => "Patron not found." } ); + } + + my $account = $patron->account; + my $body = $c->validation->param('body'); + + return try { + my $credit_type = $body->{credit_type} || 'payment'; # default to 'payment' + my $amount = $body->{amount}; # mandatory, validated by openapi + + unless ( $amount > 0 ) { # until we support newer JSON::Validator and thus minimumExclusive + Koha::Exceptions::BadParameter->throw( { parameter => 'amount' } ); + } + + # read the rest of the params + my $payment_type = $body->{payment_type}; + my $description = $body->{description}; + my $note = $body->{note}; + + my $credit = $account->add_credit( + { amount => $amount, + credit_type => $credit_type, + payment_type => $payment_type, + description => $description, + note => $note, + user_id => $user->id + } + ); + $credit->discard_changes; + + my $date = $body->{date}; + $credit->date( $date )->store + if $date; + + my $debits_ids = $body->{account_lines_ids}; + my $debits = Koha::Account::Lines->search({ accountlines_id => { -in => $debits_ids } }) + if $debits_ids; + + my $outstanding_credit = $credit->amountoutstanding; + if ($debits) { + # pay them! + $outstanding_credit = $credit->apply({ debits => $debits, offset_type => 'payment' }); + } + + if ($outstanding_credit) { + my $outstanding_debits = $account->outstanding_debits; + $credit->apply({ debits => $outstanding_debits, offset_type => 'payment' }); + } + + return $c->render( status => 200, openapi => { account_line_id => $credit->id } ); + } + catch { + if ( blessed $_ && $_->can('rethrow') ) { + return $c->render( + status => 400, + openapi => { error => "$_" } + ); + } + else { + # Exception, rely on the stringified exception + return $c->render( + status => 500, + openapi => { error => "Something went wrong, check the logs" } + ); + } + }; +} + + =head3 _to_api Helper function that maps unblessed Koha::Account::Line objects diff --git a/t/db_dependent/api/v1/patrons_accounts.t b/t/db_dependent/api/v1/patrons_accounts.t index 977890c..bf8b9de 100644 --- a/t/db_dependent/api/v1/patrons_accounts.t +++ b/t/db_dependent/api/v1/patrons_accounts.t @@ -17,7 +17,7 @@ use Modern::Perl; -use Test::More tests => 1; +use Test::More tests => 2; use Test::Mojo; use Test::Warn; @@ -45,8 +45,8 @@ subtest 'get_balance() tests' => sub { $schema->storage->txn_begin; - my ( $patron_id, $session_id ) = create_user_and_session({ authorized => 0 }); - my $patron = Koha::Patrons->find($patron_id); + my ( $patron, $session_id ) = create_user_and_session({ authorized => 0 }); + my $patron_id = $patron->id; my $account = $patron->account; my $tx = $t->ua->build_tx(GET => "/api/v1/patrons/$patron_id/account"); @@ -147,32 +147,112 @@ subtest 'get_balance() tests' => sub { $schema->storage->txn_rollback; }; +subtest 'add_credit() tests' => sub { + + plan tests => 17; + + $schema->storage->txn_begin; + + my ( $patron, $session_id ) = create_user_and_session( { authorized => 1 } ); + my $patron_id = $patron->id; + my $account = $patron->account; + + is( $account->outstanding_debits->count, 0, 'No outstanding debits for patron' ); + is( $account->outstanding_credits->count, 0, 'No outstanding credits for patron' ); + + my $credit = { amount => 100 }; + + my $tx = $t->ua->build_tx( + POST => "/api/v1/patrons/$patron_id/account/credits" => json => $credit ); + $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); + $tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); + $t->request_ok($tx)->status_is(200)->json_has('/account_line_id'); + + my $outstanding_credits = $account->outstanding_credits; + is( $outstanding_credits->count, 1 ); + is( $outstanding_credits->total_outstanding, -100 ); + + my $debit_1 = Koha::Account::Line->new( + { borrowernumber => $patron->borrowernumber, + date => \'NOW()', + amount => 10, + description => "A description", + accounttype => "N", # New card + amountoutstanding => 10, + manager_id => $patron->borrowernumber, + } + )->store(); + my $debit_2 = Koha::Account::Line->new( + { borrowernumber => $patron->borrowernumber, + date => \'NOW()', + amount => 15, + description => "A description", + accounttype => "N", # New card + amountoutstanding => 15, + manager_id => $patron->borrowernumber, + } + )->store(); + + is( $account->outstanding_debits->total_outstanding, 25 ); + $tx = $t->ua->build_tx( + POST => "/api/v1/patrons/$patron_id/account/credits" => json => $credit ); + $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); + $tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); + $t->request_ok($tx)->status_is(200)->json_has('/account_line_id'); + + is( $account->outstanding_debits->total_outstanding, + 0, "Debits have been cancelled automatically" ); + + my $debit_3 = Koha::Account::Line->new( + { borrowernumber => $patron->borrowernumber, + date => \'NOW()', + amount => 100, + description => "A description", + accounttype => "N", # New card + amountoutstanding => 100, + manager_id => $patron->borrowernumber, + } + )->store(); + + $credit = { + amount => 35, + account_lines_ids => [ $debit_1->id, $debit_2->id, $debit_3->id ] + }; + + $tx = $t->ua->build_tx( + POST => "/api/v1/patrons/$patron_id/account/credits" => json => $credit ); + $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); + $tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); + $t->request_ok($tx)->status_is(200)->json_has('/account_line_id'); + + my $outstanding_debits = $account->outstanding_debits; + is( $outstanding_debits->total_outstanding, 65 ); + is( $outstanding_debits->count, 1 ); + + $schema->storage->txn_rollback; +}; + sub create_user_and_session { my $args = shift; - my $flags = ( $args->{authorized} ) ? 16 : 0; + my $flags = ( $args->{authorized} ) ? 2**10 : 0; - my $user = $builder->build( + my $patron = $builder->build_object( { - source => 'Borrower', + class => 'Koha::Patrons', value => { - flags => $flags, - gonenoaddress => 0, - lost => 0, - email => 'nobody@example.com', - emailpro => 'nobody@example.com', - B_email => 'nobody@example.com' + flags => $flags } } ); # Create a session for the authorized user my $session = C4::Auth::get_session(''); - $session->param( 'number', $user->{borrowernumber} ); - $session->param( 'id', $user->{userid} ); + $session->param( 'number', $patron->id ); + $session->param( 'id', $patron->userid ); $session->param( 'ip', '127.0.0.1' ); $session->param( 'lasttime', time() ); $session->flush; - return ( $user->{borrowernumber}, $session->id ); + return ( $patron, $session->id ); } -- 2.1.4