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 <martin.renvoize@ptfs-europe.com>

Signed-off-by: Josef Moravec <josef.moravec@gmail.com>

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
This commit is contained in:
Tomas Cohen Arazi 2018-07-10 14:54:17 -03:00 committed by Nick Clemens
parent 566badf12a
commit d19d8ec049
2 changed files with 182 additions and 15 deletions

View File

@ -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

View File

@ -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 );
}