From 31f854cee8adb6bb64569ecbe46f9d3cfc001eaa Mon Sep 17 00:00:00 2001 From: Alex Arnaud Date: Thu, 12 Nov 2015 14:44:47 +0100 Subject: [PATCH] Bug 15165 - Add API routes to pay accountlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Tested by Stockholm University Library. Works as intended in regards to accountlines (list, edit, pay). It is not possible to pay specified/partial amounts (for one or more accountlines). At least not that we have managed. I’m unsure if this is the intended purpose of this bug however, or if this is a possible future enhancement? Maybe Julian can comment on this? I’m going to set the status to Signed off as the basic functionality works, but if anyone disagrees feel free to set it back to Needs sign off! --- Koha/REST/V1/Accountline.pm | 83 +++++++++++++++ api/v1/definitions/amountpayed.json | 9 ++ api/v1/definitions/index.json | 2 + api/v1/definitions/partialpayAccountlineBody.json | 11 ++ api/v1/swagger.json | 115 +++++++++++++++++++++ t/db_dependent/api/v1/accountlines.t | 114 +++++++++++++++++++- 6 files changed, 329 insertions(+), 5 deletions(-) create mode 100644 api/v1/definitions/amountpayed.json create mode 100644 api/v1/definitions/partialpayAccountlineBody.json diff --git a/Koha/REST/V1/Accountline.pm b/Koha/REST/V1/Accountline.pm index 7f33511..6e802a2 100644 --- a/Koha/REST/V1/Accountline.pm +++ b/Koha/REST/V1/Accountline.pm @@ -20,7 +20,10 @@ use Modern::Perl; use Mojo::Base 'Mojolicious::Controller'; use C4::Auth qw( haspermission ); +use C4::Accounts qw( makepayment makepartialpayment recordpayment ); +use C4::Members qw( GetMember ); use Koha::Accountlines; +use Scalar::Util qw( looks_like_number ); sub list { my ($c, $args, $cb) = @_; @@ -58,4 +61,84 @@ sub edit { return $c->$cb($accountline->unblessed(), 200); } + +sub pay { + my ($c, $args, $cb) = @_; + + my $user = $c->stash('koha.user'); + unless ($user && haspermission($user->userid, {updatecharges => 1})) { + return $c->$cb({error => "You don't have the required permission"}, 403); + } + + my $accountline = Koha::Accountlines->find($args->{accountlines_id}); + unless ($accountline) { + return $c->$cb({error => "Accountline not found"}, 404); + } + + makepayment($accountline->accountlines_id, + $accountline->borrowernumber, + $accountline->accountno, + $accountline->amount); + + $accountline = Koha::Accountlines->find($args->{accountlines_id}); + return $c->$cb($accountline->unblessed(), 200); +} + +sub partialpay { + my ($c, $args, $cb) = @_; + + my $user = $c->stash('koha.user'); + unless ($user && haspermission($user->userid, {updatecharges => 1})) { + return $c->$cb({error => "You don't have the required permission"}, 403); + } + + my $accountline = Koha::Accountlines->find($args->{accountlines_id}); + unless ($accountline) { + return $c->$cb({error => "Accountline not found"}, 404); + } + + my $body = $c->req->json; + my $amount = $body->{amount}; + my $note = $body->{note} || ''; + + unless ($amount && looks_like_number($amount)) { + return $c->$cb({error => "Invalid amount"}, 400); + } + + makepartialpayment($accountline->accountlines_id, + $accountline->borrowernumber, + $accountline->accountno, + $amount, '', '', $note); + + $accountline = Koha::Accountlines->find($args->{accountlines_id}); + return $c->$cb($accountline->unblessed(), 200); +} + + +sub payamount { + my ($c, $args, $cb) = @_; + + my $user = $c->stash('koha.user'); + unless ($user && haspermission($user->userid, {updatecharges => 1})) { + return $c->$cb({error => "You don't have the required permission"}, 403); + } + + my $borrower = GetMember(borrowernumber => $args->{borrowernumber}); + unless ($borrower) { + return $c->$cb({error => "Borrower not found"}, 404); + } + + my $body = $c->req->json; + my $amount = $body->{amount}; + my $note = $body->{note} || ''; + + unless ($amount && looks_like_number($amount)) { + return $c->$cb({error => "Invalid amount"}, 400); + } + + recordpayment($borrower->{borrowernumber}, $amount, '', $note); + + return $c->$cb({amount => $amount}, 200); +} + 1; diff --git a/api/v1/definitions/amountpayed.json b/api/v1/definitions/amountpayed.json new file mode 100644 index 0000000..33c5550 --- /dev/null +++ b/api/v1/definitions/amountpayed.json @@ -0,0 +1,9 @@ +{ + "type": "object", + "properties": { + "amount": { + "description": "Amount payed", + "type": "integer" + } + } +} diff --git a/api/v1/definitions/index.json b/api/v1/definitions/index.json index 80b43e0..ea0fdf2 100644 --- a/api/v1/definitions/index.json +++ b/api/v1/definitions/index.json @@ -2,5 +2,7 @@ "patron": { "$ref": "patron.json" }, "accountline": { "$ref": "accountline.json" }, "editAccountlineBody": { "$ref": "editAccountlineBody.json" }, + "partialpayAccountlineBody": { "$ref": "partialpayAccountlineBody.json" }, + "amountpayed": { "$ref": "amountpayed.json" }, "error": { "$ref": "error.json" } } diff --git a/api/v1/definitions/partialpayAccountlineBody.json b/api/v1/definitions/partialpayAccountlineBody.json new file mode 100644 index 0000000..a49f229 --- /dev/null +++ b/api/v1/definitions/partialpayAccountlineBody.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "properties": { + "amount": { + "description": "Amount to pay" + }, + "note": { + "description": "Payment note" + } + } +} diff --git a/api/v1/swagger.json b/api/v1/swagger.json index f8ba465..996ddf4 100644 --- a/api/v1/swagger.json +++ b/api/v1/swagger.json @@ -140,6 +140,121 @@ } } } + }, + "/accountlines/{accountlines_id}/payment": { + "put": { + "operationId": "payAccountlines", + "tags": ["accountlines"], + "produces": [ + "application/json" + ], + "parameters": [ + { "$ref": "#/parameters/accountlinesIdPathParam" } + ], + "produces": ["application/json"], + "responses": { + "200": { + "description": "Payed accountline", + "schema": { "$ref": "#/definitions/accountline" } + }, + "400": { + "description": "Missing or wrong parameters", + "schema": { "$ref": "#/definitions/error" } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "#/definitions/error" + } + }, + "404": { + "description": "Accountline not found", + "schema": { "$ref": "#/definitions/error" } + } + } + } + }, + "/accountlines/{accountlines_id}/partialpayment": { + "put": { + "operationId": "partialpayAccountlines", + "tags": ["accountlines"], + "produces": [ + "application/json" + ], + "parameters": [ + { "$ref": "#/parameters/accountlinesIdPathParam" }, + { + "name": "body", + "in": "body", + "description": "A JSON object containing fields to modify", + "required": true, + "schema": { "$ref": "#/definitions/partialpayAccountlineBody" } + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "Payed accountline", + "schema": { "$ref": "#/definitions/accountline" } + }, + "400": { + "description": "Missing or wrong parameters", + "schema": { "$ref": "#/definitions/error" } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "#/definitions/error" + } + }, + "404": { + "description": "Accountline not found", + "schema": { "$ref": "#/definitions/error" } + } + } + } + }, + "/accountlines/{borrowernumber}/amountpayment": { + "put": { + "operationId": "payamountAccountlines", + "tags": ["accountlines"], + "produces": [ + "application/json" + ], + "parameters": [ + { "$ref": "#/parameters/borrowernumberPathParam" }, + { + "name": "body", + "in": "body", + "description": "A JSON object containing fields to modify", + "required": true, + "schema": { "$ref": "#/definitions/partialpayAccountlineBody" } + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "Amount payed", + "schema": { "$ref": "#/definitions/amountpayed" } + }, + "400": { + "description": "Missing or wrong parameters", + "schema": { "$ref": "#/definitions/error" } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "#/definitions/error" + } + }, + "404": { + "description": "Borrower not found", + "schema": { "$ref": "#/definitions/error" } + } + } + } } }, "definitions": { diff --git a/t/db_dependent/api/v1/accountlines.t b/t/db_dependent/api/v1/accountlines.t index 30b7a08..6d866bf 100644 --- a/t/db_dependent/api/v1/accountlines.t +++ b/t/db_dependent/api/v1/accountlines.t @@ -17,7 +17,7 @@ use Modern::Perl; -use Test::More tests => 18; +use Test::More tests => 46; use Test::Mojo; use t::lib::TestBuilder; @@ -44,6 +44,12 @@ $t->get_ok('/api/v1/accountlines') $t->put_ok("/api/v1/accountlines/11224409" => json => {'amount' => -5}) ->status_is(403); +$t->put_ok("/api/v1/accountlines/11224408/payment") + ->status_is(403); + +$t->put_ok("/api/v1/accountlines/11224407/partialpayment" => json => {'amount' => 8}) + ->status_is(403); + my $loggedinuser = $builder->build({ source => 'Borrower', value => { @@ -73,8 +79,8 @@ my $borrowernumber2 = $borrower2->{borrowernumber}; $dbh->do(q| DELETE FROM accountlines |); $dbh->do(q| - INSERT INTO accountlines (borrowernumber, amount, accounttype) - VALUES (?, 20, 'A'), (?, 40, 'F'), (?, 80, 'F'), (?, 10, 'F') + INSERT INTO accountlines (borrowernumber, amount, accounttype, amountoutstanding) + VALUES (?, 20, 'A', 20), (?, 40, 'F', 40), (?, 80, 'F', 80), (?, 10, 'F', 10) |, undef, $borrowernumber, $borrowernumber, $borrowernumber, $borrowernumber2); my $session = C4::Auth::get_session(''); @@ -113,7 +119,6 @@ my $put_data = { $tx = $t->ua->build_tx( PUT => "/api/v1/accountlines/11224409" - => {Accept => '*/*'} => json => $put_data); $tx->req->cookies({name => 'CGISESSID', value => $session->id}); $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); @@ -124,7 +129,6 @@ my $accountline_to_edit = Koha::Accountlines->search({'borrowernumber' => $borro $tx = $t->ua->build_tx( PUT => "/api/v1/accountlines/$accountline_to_edit->{accountlines_id}" - => {Accept => '*/*'} => json => $put_data); $tx->req->cookies({name => 'CGISESSID', value => $session->id}); $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); @@ -138,5 +142,105 @@ is($accountline_edited->{amountoutstanding}, '-19.000000'); # Payment tests +$tx = $t->ua->build_tx(PUT => "/api/v1/accountlines/4562765765/payment"); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(404); + +my $accountline_to_pay = Koha::Accountlines->search({'borrowernumber' => $borrowernumber, 'amount' => 20})->unblessed()->[0]; +$tx = $t->ua->build_tx(PUT => "/api/v1/accountlines/$accountline_to_pay->{accountlines_id}/payment"); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(200); + +my $accountline_payed = Koha::Accountlines->search({'borrowernumber' => $borrowernumber, 'amount' => -20})->unblessed()->[0]; +ok($accountline_payed); + +# Partial payment tests +$put_data = { + 'amount' => 17, + 'note' => 'Partial payment' +}; + +$tx = $t->ua->build_tx( + PUT => "/api/v1/accountlines/11224419/partialpayment" + => json => $put_data); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(404); + +my $accountline_to_partiallypay = Koha::Accountlines->search({'borrowernumber' => $borrowernumber, 'amount' => 80})->unblessed()->[0]; + +$tx = $t->ua->build_tx(PUT => "/api/v1/accountlines/$accountline_to_partiallypay->{accountlines_id}/partialpayment" => json => {amount => 'foo'}); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(400); + +$tx = $t->ua->build_tx(PUT => "/api/v1/accountlines/$accountline_to_partiallypay->{accountlines_id}/partialpayment" => json => $put_data); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(200); + +$accountline_to_partiallypay = Koha::Accountlines->search({'borrowernumber' => $borrowernumber, 'amount' => 80})->unblessed()->[0]; +is($accountline_to_partiallypay->{amountoutstanding}, '63.000000'); + +my $accountline_partiallypayed = Koha::Accountlines->search({'borrowernumber' => $borrowernumber, 'amount' => 17})->unblessed()->[0]; +ok($accountline_partiallypayed); + +# Pay amount tests +my $borrower3 = $builder->build({ + source => 'Borrower', + value => { + branchcode => $branchcode, + categorycode => $categorycode, + } +}); +my $borrowernumber3 = $borrower3->{borrowernumber}; + +$dbh->do(q| + INSERT INTO accountlines (borrowernumber, amount, accounttype, amountoutstanding) + VALUES (?, 26, 'A', 26) + |, undef, $borrowernumber3); + +$t->put_ok("/api/v1/accountlines/$borrowernumber3/amountpayment" => json => {'amount' => 8}) + ->status_is(403); + +my $put_data2 = { + 'amount' => 24, + 'note' => 'Partial payment' +}; + +$tx = $t->ua->build_tx(PUT => "/api/v1/accountlines/8789798797/amountpayment" => json => $put_data2); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(404); + +$tx = $t->ua->build_tx(PUT => "/api/v1/accountlines/$borrowernumber3/amountpayment" => json => {amount => 0}); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(400); + +$tx = $t->ua->build_tx(PUT => "/api/v1/accountlines/$borrowernumber3/amountpayment" => json => {amount => 'foo'}); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(400); + +$tx = $t->ua->build_tx(PUT => "/api/v1/accountlines/$borrowernumber3/amountpayment" => json => $put_data2); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(200); + +$accountline_partiallypayed = Koha::Accountlines->search({'borrowernumber' => $borrowernumber3, 'amount' => 26})->unblessed()->[0]; + +is($accountline_partiallypayed->{amountoutstanding}, '2.000000'); $dbh->rollback; -- 1.7.10.4