From 3d7cc8d281c3ea158539f85689a8b8e4330102ec 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 --- 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; -- 2.1.4