Bugzilla – Attachment 55642 Details for
Bug 16652
Omnibus: RestfulAPI supporting services for Vufind and for xnciptoolkit drivers
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 16652: RestfulAPI supporting services for Vufind and for xnciptoolkit drivers
Bug-16652-RestfulAPI-supporting-services-for-Vufin.patch (text/plain), 145.20 KB, created by
Jiri Kozlovsky
on 2016-09-18 09:47:30 UTC
(
hide
)
Description:
Bug 16652: RestfulAPI supporting services for Vufind and for xnciptoolkit drivers
Filename:
MIME Type:
Creator:
Jiri Kozlovsky
Created:
2016-09-18 09:47:30 UTC
Size:
145.20 KB
patch
obsolete
>From 643cfcb3481eb73a1af31f4f25a871c6dff1d81d Mon Sep 17 00:00:00 2001 >From: =?UTF-8?q?Ji=C5=99=C3=AD=20Kozlovsk=C3=BD?= <mail@jkozlovsky.cz> >Date: Sun, 31 Jul 2016 11:19:09 +0200 >Subject: [PATCH] Bug 16652: RestfulAPI supporting services for Vufind and for > xnciptoolkit drivers > >Fixed checkout endpoint for null itemnumbers & renamed "borrowers" tags >to "patrons" > >Note that this patch is about to be used with the current Koha stable >version, which has not implemented Bug 16202, Bug 16699 and Bug 14868 just yet. > >We want to be capable of patching the stable so that it can contain all >the services needed with basically zero-effort. > >Lari: I hope those patches are going to be backported to current stable >soon so that we don't have to do this hassle. And, the Swagger-UI is >quite awesome! :) >--- > Koha/Accountline.pm | 30 + > Koha/Accountlines.pm | 35 ++ > Koha/Item.pm | 166 ++++++ > Koha/Item/Availability.pm | 283 ++++++++++ > Koha/REST/V1/Accountline.pm | 144 +++++ > Koha/REST/V1/Availability.pm | 99 ++++ > Koha/REST/V1/Biblio.pm | 76 +++ > Koha/REST/V1/Checkout.pm | 159 ++++++ > Koha/REST/V1/Item.pm | 54 ++ > Koha/REST/V1/Library.pm | 42 ++ > Koha/REST/V1/Patron.pm | 157 +++++- > api/v1/definitions/accountline.json | 56 ++ > api/v1/definitions/amountpaid.json | 9 + > api/v1/definitions/availabilities.json | 4 + > api/v1/definitions/availability.json | 57 ++ > api/v1/definitions/availabilitystatus.json | 16 + > api/v1/definitions/biblio.json | 65 +++ > api/v1/definitions/checkout.json | 51 ++ > api/v1/definitions/checkouts.json | 6 + > api/v1/definitions/editAccountlineBody.json | 17 + > api/v1/definitions/index.json | 12 + > api/v1/definitions/item.json | 177 ++++++ > api/v1/definitions/libraries.json | 4 + > api/v1/definitions/library.json | 77 +++ > api/v1/definitions/partialpayAccountlineBody.json | 11 + > api/v1/swagger.json | 652 +++++++++++++++++++++- > t/Koha/Item/Availability.t | 71 +++ > t/db_dependent/Items.t | 111 +++- > t/db_dependent/api/v1/accountlines.t | 246 ++++++++ > t/db_dependent/api/v1/availability.t | 289 ++++++++++ > t/db_dependent/api/v1/biblios.t | 116 ++++ > t/db_dependent/api/v1/checkouts.t | 222 ++++++++ > t/db_dependent/api/v1/checkoutshistory.t | 160 ++++++ > t/db_dependent/api/v1/items.t | 117 ++++ > t/db_dependent/api/v1/libraries.t | 63 +++ > t/db_dependent/api/v1/patrons.t | 167 +++++- > 36 files changed, 4003 insertions(+), 18 deletions(-) > create mode 100644 Koha/Accountline.pm > create mode 100644 Koha/Accountlines.pm > create mode 100644 Koha/Item/Availability.pm > create mode 100644 Koha/REST/V1/Accountline.pm > create mode 100644 Koha/REST/V1/Availability.pm > create mode 100644 Koha/REST/V1/Biblio.pm > create mode 100644 Koha/REST/V1/Checkout.pm > create mode 100644 Koha/REST/V1/Item.pm > create mode 100644 Koha/REST/V1/Library.pm > create mode 100644 api/v1/definitions/accountline.json > create mode 100644 api/v1/definitions/amountpaid.json > create mode 100644 api/v1/definitions/availabilities.json > create mode 100644 api/v1/definitions/availability.json > create mode 100644 api/v1/definitions/availabilitystatus.json > create mode 100644 api/v1/definitions/biblio.json > create mode 100644 api/v1/definitions/checkout.json > create mode 100644 api/v1/definitions/checkouts.json > create mode 100644 api/v1/definitions/editAccountlineBody.json > create mode 100644 api/v1/definitions/item.json > create mode 100644 api/v1/definitions/libraries.json > create mode 100644 api/v1/definitions/library.json > create mode 100644 api/v1/definitions/partialpayAccountlineBody.json > create mode 100644 t/Koha/Item/Availability.t > create mode 100644 t/db_dependent/api/v1/accountlines.t > create mode 100644 t/db_dependent/api/v1/availability.t > create mode 100644 t/db_dependent/api/v1/biblios.t > create mode 100644 t/db_dependent/api/v1/checkouts.t > create mode 100644 t/db_dependent/api/v1/checkoutshistory.t > create mode 100644 t/db_dependent/api/v1/items.t > create mode 100644 t/db_dependent/api/v1/libraries.t > >diff --git a/Koha/Accountline.pm b/Koha/Accountline.pm >new file mode 100644 >index 0000000..04254d7 >--- /dev/null >+++ b/Koha/Accountline.pm >@@ -0,0 +1,30 @@ >+package Koha::Accountline; >+ >+# Copyright 2015 BibLibre >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Koha::Database; >+ >+use base qw(Koha::Object); >+ >+sub _type { >+ return 'Accountline'; >+} >+ >+1; >diff --git a/Koha/Accountlines.pm b/Koha/Accountlines.pm >new file mode 100644 >index 0000000..6d84094 >--- /dev/null >+++ b/Koha/Accountlines.pm >@@ -0,0 +1,35 @@ >+package Koha::Accountlines; >+ >+# Copyright 2015 BibLibre >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Koha::Database; >+use Koha::Accountline; >+ >+use base qw(Koha::Objects); >+ >+sub _type { >+ return 'Accountline'; >+} >+ >+sub object_class { >+ return 'Koha::Accountline'; >+} >+ >+1; >diff --git a/Koha/Item.pm b/Koha/Item.pm >index 0bbc493..bf4da56 100644 >--- a/Koha/Item.pm >+++ b/Koha/Item.pm >@@ -39,6 +39,160 @@ Koha::Item - Koha Item object class > > =cut > >+=head3 availabilities >+ >+my $available = $item->availabilities(); >+ >+Gets different availability types, generally, without considering patron status. >+ >+Returns HASH containing Koha::Item::Availability objects for each availability >+type. Currently implemented availabilities are: >+ * hold >+ * checkout >+ * local_use >+ * onsite_checkout >+ >+=cut >+ >+sub availabilities { >+ my ( $self, $params ) = @_; >+ >+ my $availabilities; # HASH containing different types of availabilities >+ my $availability = Koha::Item::Availability->new->set_available; >+ >+ $availability->set_unavailable("withdrawn") if $self->withdrawn; >+ $availability->set_unavailable("itemlost") if $self->itemlost; >+ $availability->set_unavailable("restricted") if $self->restricted; >+ >+ if ($self->damaged) { >+ if (C4::Context->preference('AllowHoldsOnDamagedItems')) { >+ $availability->add_description("damaged"); >+ } else { >+ $availability->set_unavailable("damaged"); >+ } >+ } >+ >+ my $itemtype; >+ if (C4::Context->preference('item-level_itypes')) { >+ $itemtype = Koha::ItemTypes->find( $self->itype ); >+ } else { >+ my $biblioitem = Koha::Biblioitems->find( $self->biblioitemnumber ); >+ $itemtype = Koha::ItemTypes->find( $biblioitem->itemype ); >+ } >+ >+ if ($self->notforloan > 0 || $itemtype && $itemtype->notforloan) { >+ $availability->set_unavailable("notforloan"); >+ } elsif ($self->notforloan < 0) { >+ $availability->set_unavailable("ordered"); >+ } >+ >+ # Hold >+ $availabilities->{'hold'} = $availability->clone; >+ >+ # Checkout >+ if ($self->onloan) { >+ my $issue = Koha::Issues->search({ itemnumber => $self->itemnumber })->next; >+ $availability->set_unavailable("onloan", $issue->date_due) if $issue; >+ } >+ >+ if (Koha::Holds->search( [ >+ { itemnumber => $self->itemnumber }, >+ { found => [ '=', 'W', 'T' ] } >+ ])->count()) { >+ $availability->set_unavailable("reserved"); >+ } >+ >+ $availabilities->{'checkout'} = $availability->clone; >+ >+ # Local Use, >+ if (grep(/^notforloan$/, @{$availability->{description}}) >+ && @{$availability->{description}} == 1) { >+ $availabilities->{'local_use'} = $availability->clone->set_available >+ ->del_description("notforloan"); >+ } else { >+ $availabilities->{'local_use'} = $availability->clone >+ ->del_description("notforloan"); >+ } >+ >+ # On-site checkout >+ if (!C4::Context->preference('OnSiteCheckouts')) { >+ $availabilities->{'onsite_checkout'} >+ = Koha::Item::Availability->new >+ ->set_unavailable("onsite_checkouts_disabled"); >+ } else { >+ $availabilities->{'onsite_checkout'} >+ = $availabilities->{'local_use'}->clone; >+ } >+ >+ return $availabilities; >+} >+ >+=head3 availability_for_checkout >+ >+my $available = $item->availability_for_checkout(); >+ >+Gets checkout availability of the item. This subroutine does not check patron >+status, instead the purpose is to check general availability for this item. >+ >+Returns Koha::Item::Availability object. >+ >+=cut >+ >+sub availability_for_checkout { >+ my ( $self ) = @_; >+ >+ return $self->availabilities->{'checkout'}; >+} >+ >+=head3 availability_for_local_use >+ >+my $available = $item->availability_for_local_use(); >+ >+Gets local use availability of the item. >+ >+Returns Koha::Item::Availability object. >+ >+=cut >+ >+sub availability_for_local_use { >+ my ( $self ) = @_; >+ >+ return $self->availabilities->{'local_use'}; >+} >+ >+=head3 availability_for_onsite_checkout >+ >+my $available = $item->availability_for_onsite_checkout(); >+ >+Gets on-site checkout availability of the item. >+ >+Returns Koha::Item::Availability object. >+ >+=cut >+ >+sub availability_for_onsite_checkout { >+ my ( $self ) = @_; >+ >+ return $self->availabilities->{'onsite_checkout'}; >+} >+ >+=head3 availability_for_reserve >+ >+my $available = $item->availability_for_reserve(); >+ >+Gets reserve availability of the item. This subroutine does not check patron >+status, instead the purpose is to check general availability for this item. >+ >+Returns Koha::Item::Availability object. >+ >+=cut >+ >+sub availability_for_reserve { >+ my ( $self ) = @_; >+ >+ return $self->availabilities->{'hold'}; >+} >+ > =head3 effective_itemtype > > Returns the itemtype for the item based on whether item level itemtypes are set or not. >@@ -51,6 +205,18 @@ sub effective_itemtype { > return $self->_result()->effective_itemtype(); > } > >+=head3 hold_queue_length >+ >+=cut >+ >+sub hold_queue_length { >+ my ( $self ) = @_; >+ >+ my $reserves = Koha::Holds->search({ itemnumber => $self->itemnumber }); >+ return $reserves->count() if $reserves; >+ return 0; >+} >+ > =head3 home_branch > > =cut >diff --git a/Koha/Item/Availability.pm b/Koha/Item/Availability.pm >new file mode 100644 >index 0000000..3cacd29 >--- /dev/null >+++ b/Koha/Item/Availability.pm >@@ -0,0 +1,283 @@ >+package Koha::Item::Availability; >+ >+# Copyright KohaSuomi 2016 >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Storable qw(dclone); >+ >+=head1 NAME >+ >+Koha::Item::Availability - Koha Item Availability object class >+ >+=head1 SYNOPSIS >+ >+ my $item = Koha::Items->find(1337); >+ my $availabilities = $item->availabilities(); >+ # ref($availabilities) eq 'HASH' >+ # ref($availabilities->{'checkout'}) eq 'Koha::Item::Availability' >+ >+ print "Available for checkout!" if $availabilities->{'checkout'}->{available}; >+ >+=head1 DESCRIPTION >+ >+This class holds item availability information. >+ >+See Koha::Item for availability subroutines. >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 new >+ >+Returns a new Koha::Item::Availability object. >+ >+=cut >+ >+sub new { >+ my ( $class ) = @_; >+ >+ my $self = { >+ description => [], >+ availability_needs_confirmation => undef, >+ available => undef, >+ expected_available => undef, >+ }; >+ >+ bless( $self, $class ); >+} >+ >+=head3 add_description >+ >+$availability->add_description("notforloan"); >+$availability->add_description("withdrawn); >+ >+# $availability->{description} = ["notforloan", "withdrawn"] >+ >+Pushes a new description to $availability object. Does not duplicate existing >+descriptions. >+ >+Returns updated Koha::Item::Availability object. >+ >+=cut >+ >+sub add_description { >+ my ($self, $description) = @_; >+ >+ return $self unless $description; >+ >+ if (ref($description) eq 'ARRAY') { >+ foreach my $desc (@$description) { >+ if (grep(/^$desc$/, @{$self->{description}})){ >+ next; >+ } >+ push $self->{description}, $desc; >+ } >+ } else { >+ if (!grep(/^$description$/, @{$self->{description}})){ >+ push $self->{description}, $description; >+ } >+ } >+ >+ return $self; >+} >+ >+=head3 clone >+ >+$availability_cloned = $availability->clone; >+$availability->set_unavailable; >+ >+# $availability_cloned->{available} != $availability->{available} >+ >+Clones the Koha::Item::Availability object. >+ >+Returns cloned object. >+ >+=cut >+ >+sub clone { >+ my ( $self ) = @_; >+ >+ return dclone($self); >+} >+ >+=head3 del_description >+ >+$availability->add_description(["notforloan", "withdrawn", "itemlost", "restricted"]); >+$availability->del_description("withdrawn"); >+ >+# $availability->{description} == ["notforloan", "itemlost", "restricted"] >+$availability->del_description(["withdrawn", "restricted"]); >+# $availability->{description} == ["itemlost"] >+ >+Deletes an availability description(s) if it exists. >+ >+Returns (possibly updated) Koha::Item::Availability object. >+ >+=cut >+ >+sub del_description { >+ my ($self, $description) = @_; >+ >+ return $self unless $description; >+ >+ my @updated; >+ if (ref($description) eq 'ARRAY') { >+ foreach my $desc (@$description) { >+ @updated = grep(!/^$desc$/, @{$self->{description}}); >+ } >+ } else { >+ @updated = grep(!/^$description$/, @{$self->{description}}); >+ } >+ $self->{description} = \@updated; >+ >+ return $self; >+} >+ >+=head3 hash_description >+ >+$availability->add_description(["notforloan", "withdrawn"]); >+$availability->has_description("withdrawn"); # 1 >+$availability->has_description(["notforloan", "withdrawn"]); # 1 >+$availability->has_description("itemlost"); # 0 >+ >+Finds description(s) in availability descriptions. >+ >+Returns 1 if found, 0 otherwise. >+ >+=cut >+ >+sub has_description { >+ my ($self, $description) = @_; >+ >+ return 0 unless $description; >+ >+ my @found; >+ if (ref($description) eq 'ARRAY') { >+ foreach my $desc (@$description) { >+ if (!grep(/^$desc$/, @{$self->{description}})){ >+ return 0; >+ } >+ } >+ } else { >+ if (!grep(/^$description$/, @{$self->{description}})){ >+ return 0; >+ } >+ } >+ >+ return 1; >+} >+ >+=head3 reset >+ >+$availability->reset; >+ >+Resets the object. >+ >+=cut >+ >+sub reset { >+ my ( $self ) = @_; >+ >+ $self->{available} = undef; >+ $self->{availability_needs_confirmation} = undef; >+ $self->{expected_available} = undef; >+ $self->{description} = []; >+ return $self; >+} >+ >+=head3 set_available >+ >+$availability->set_available; >+ >+Sets the Koha::Item::Availability object status to available. >+ $availability->{available} == 1 >+ >+Overrides old availability status, but does not override other stored data in >+the object. Create a new Koha::Item::Availability object to get a fresh start. >+Appends any previously defined availability descriptions with add_description(). >+ >+Returns updated Koha::Item::Availability object. >+ >+=cut >+ >+sub set_available { >+ my ($self, $description) = @_; >+ >+ return $self->_update_availability_status(1, 0, $description); >+} >+ >+=head3 set_needs_confirmation >+ >+$availability->set_needs_confirmation("unbelieveable_reason", "2016-07-07"); >+ >+Sets the Koha::Item::Availability object status to unavailable, >+but needs confirmation. >+ $availability->{available} == 0 >+ $availability->{availability_needs_confirmation} == 1 >+ >+Overrides old availability statuses, but does not override other stored data in >+the object. Create a new Koha::Item::Availability object to get a fresh start. >+Appends any previously defined availability descriptions with add_description(). >+Allows you to define expected availability date in C<$expected>. >+ >+Returns updated Koha::Item::Availability object. >+ >+=cut >+ >+sub set_needs_confirmation { >+ my ($self, $description, $expected) = @_; >+ >+ return $self->_update_availability_status(0, 1, $description, $expected); >+} >+ >+=head3 set_unavailable >+ >+$availability->set_unavailable("onloan", "2016-07-07"); >+ >+Sets the Koha::Item::Availability object status to unavailable. >+ $availability->{available} == 0 >+ >+Overrides old availability status, but does not override other stored data in >+the object. Create a new Koha::Item::Availability object to get a fresh start. >+Appends any previously defined availability descriptions with add_description(). >+Allows you to define expected availability date in C<$expected>. >+ >+Returns updated Koha::Item::Availability object. >+ >+=cut >+ >+sub set_unavailable { >+ my ($self, $description, $expected) = @_; >+ >+ return $self->_update_availability_status(0, 0, $description, $expected); >+} >+ >+sub _update_availability_status { >+ my ( $self, $available, $needs, $desc, $expected ) = @_; >+ >+ $self->{available} = $available; >+ $self->{availability_needs_confirmation} = $needs; >+ $self->{expected_available} = $expected if $expected; >+ $self->add_description($desc); >+ >+ return $self; >+} >+ >+1; >diff --git a/Koha/REST/V1/Accountline.pm b/Koha/REST/V1/Accountline.pm >new file mode 100644 >index 0000000..6e802a2 >--- /dev/null >+++ b/Koha/REST/V1/Accountline.pm >@@ -0,0 +1,144 @@ >+package Koha::REST::V1::Accountline; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+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) = @_; >+ >+ 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 $params = $c->req->params->to_hash; >+ my $accountlines = Koha::Accountlines->search($params); >+ >+ return $c->$cb($accountlines->unblessed, 200); >+} >+ >+ >+sub edit { >+ 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; >+ >+ $accountline->set( $body ); >+ $accountline->store(); >+ >+ 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/Koha/REST/V1/Availability.pm b/Koha/REST/V1/Availability.pm >new file mode 100644 >index 0000000..ee4cf10 >--- /dev/null >+++ b/Koha/REST/V1/Availability.pm >@@ -0,0 +1,99 @@ >+package Koha::REST::V1::Availability; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+use Mojo::JSON; >+ >+use Koha::Holds; >+use Koha::Items; >+ >+sub items { >+ my ($c, $args, $cb) = @_; >+ >+ my @items; >+ if ($args->{'itemnumber'}) { >+ push @items, _item_availability(@{$args->{'itemnumber'}}); >+ } >+ if ($args->{'biblionumber'}) { >+ my $found_items = Koha::Items->search({ biblionumber => { >+ '=', \@{$args->{'biblionumber'}} >+ } })->as_list(); >+ my @itemnumbers; >+ foreach my $item (@$found_items) { >+ push @itemnumbers, $item->itemnumber; >+ } >+ >+ push @items, _item_availability(@itemnumbers); >+ } >+ >+ return $c->$cb({ error => "Item(s) not found"}, 404) unless scalar @items; >+ return $c->$cb([ @items ], 200); >+} >+ >+sub _item_availability { >+ my (@itemnumbers) = @_; >+ >+ my @items; >+ >+ foreach my $itemnumber (@itemnumbers) { >+ my $item = Koha::Items->find($itemnumber); >+ >+ unless ($item) { >+ next; >+ } >+ >+ my $availabilities = _swaggerize_availabilities($item->availabilities()); >+ >+ my $holds; >+ $holds->{'hold_queue_length'} = $item->hold_queue_length(); >+ >+ my $iteminfo = { >+ itemnumber => $item->itemnumber, >+ barcode => $item->barcode, >+ biblionumber => $item->biblionumber, >+ biblioitemnumber => $item->biblioitemnumber, >+ holdingbranch => $item->holdingbranch, >+ homebranch => $item->homebranch, >+ location => $item->location, >+ itemcallnumber => $item->itemcallnumber, >+ }; >+ >+ # merge availability, hold information and item information >+ push @items, { %{$availabilities}, %{$holds}, %{$iteminfo} }; >+ } >+ >+ return @items; >+} >+ >+sub _swaggerize_availabilities { >+ my ($availabilities) = @_; >+ >+ foreach my $availability (keys $availabilities) { >+ delete $availabilities->{$availability}->{availability_needs_confirmation}; >+ $availabilities->{$availability}->{available} = >+ $availabilities->{$availability}->{available} >+ ? Mojo::JSON->true >+ : Mojo::JSON->false; >+ $availabilities->{$availability} = { %{$availabilities->{$availability}} }; >+ } >+ >+ return $availabilities; >+} >+ >+1; >diff --git a/Koha/REST/V1/Biblio.pm b/Koha/REST/V1/Biblio.pm >new file mode 100644 >index 0000000..a10a8fa >--- /dev/null >+++ b/Koha/REST/V1/Biblio.pm >@@ -0,0 +1,76 @@ >+package Koha::REST::V1::Biblio; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use strict; >+use warnings; >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+use Mojo::JSON; >+ >+use C4::Auth qw( haspermission ); >+use C4::Context; >+use C4::Items qw( GetItem GetHiddenItemnumbers ); >+ >+use Koha::Biblios; >+use Koha::Items; >+ >+sub get { >+ my ($c, $args, $cb) = @_; >+ >+ my $biblionumber = $c->param('biblionumber'); >+ my $biblio = Koha::Biblios->find($biblionumber); >+ >+ unless ($biblio) { >+ return $c->$cb({error => "Biblio not found"}, 404); >+ } >+ >+ my $items ||= Koha::Items->search( { biblionumber => $biblionumber }, { >+ columns => [qw/itemnumber/], >+ })->unblessed; >+ >+ my $user = $c->stash('koha.user'); >+ my $isStaff = haspermission($user->userid, {borrowers => 1}); >+ >+ # Hide the hidden items from all but staff >+ my $opachiddenitems = ! $isStaff >+ && ( C4::Context->preference('OpacHiddenItems') !~ /^\s*$/ ); >+ >+ if ($opachiddenitems) { >+ >+ my @hiddenitems = C4::Items::GetHiddenItemnumbers( @{$items} ); >+ >+ my @filteredItems = (); >+ >+ # Convert to a hash for quick searching >+ my %hiddenitems = map { $_ => 1 } @hiddenitems; >+ foreach my $itemnumber ( map { $_->{itemnumber} } @{$items} ) { >+ next if $hiddenitems{$itemnumber}; >+ push @filteredItems, { itemnumber => $itemnumber }; >+ } >+ >+ $items = \@filteredItems; >+ } >+ >+ $biblio = $biblio->unblessed; >+ $biblio->{items} = $items; >+ >+ return $c->$cb($biblio, 200); >+} >+ >+1; >diff --git a/Koha/REST/V1/Checkout.pm b/Koha/REST/V1/Checkout.pm >new file mode 100644 >index 0000000..7ca7da9 >--- /dev/null >+++ b/Koha/REST/V1/Checkout.pm >@@ -0,0 +1,159 @@ >+package Koha::REST::V1::Checkout; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use C4::Auth qw( haspermission ); >+use C4::Context; >+use C4::Circulation; >+use Koha::Issues; >+use Koha::OldIssues; >+ >+sub list { >+ my ($c, $args, $cb) = @_; >+ >+ my $user = $c->stash('koha.user'); >+ unless ($user && ($user->borrowernumber == $c->param('borrowernumber') >+ || haspermission($user->userid, { circulate => "circulate_remaining_permissions" }))) { >+ return $c->$cb({error => "You don't have the required permission"}, 403); >+ } >+ >+ my $borrowernumber = $c->param('borrowernumber'); >+ my $checkouts = C4::Circulation::GetIssues({ >+ borrowernumber => $borrowernumber >+ }); >+ >+ $c->$cb($checkouts, 200); >+} >+ >+sub get { >+ my ($c, $args, $cb) = @_; >+ >+ my $user = $c->stash('koha.user'); >+ >+ my $checkout_id = $args->{checkout_id}; >+ my $checkout = Koha::Issues->find($checkout_id); >+ >+ if (!$checkout) { >+ return $c->$cb({ >+ error => "Checkout doesn't exist" >+ }, 404); >+ } >+ >+ my $borrowernumber = $checkout->borrowernumber; >+ >+ unless ($user && ( $user->borrowernumber == $borrowernumber >+ || haspermission($user->userid, { circulate => "circulate_remaining_permissions" }))) { >+ return $c->$cb({error => "You don't have the required permission"}, 403); >+ } >+ >+ return $c->$cb($checkout->unblessed, 200); >+} >+ >+sub renew { >+ my ($c, $args, $cb) = @_; >+ >+ my $user = $c->stash('koha.user'); >+ >+ my $checkout_id = $args->{checkout_id}; >+ my $checkout = Koha::Issues->find($checkout_id); >+ >+ if (!$checkout) { >+ return $c->$cb({ >+ error => "Checkout doesn't exist" >+ }, 404); >+ } >+ >+ $checkout = $checkout->unblessed; >+ >+ my $borrowernumber = $checkout->borrowernumber; >+ my $itemnumber = $checkout->itemnumber; >+ >+ my $OpacRenewalAllowed; >+ if ($user->borrowernumber == $borrowernumber) { >+ $OpacRenewalAllowed = C4::Context->preference('OpacRenewalAllowed'); >+ } >+ >+ unless ($user && ($OpacRenewalAllowed >+ || haspermission($user->userid, { circulate => "circulate_remaining_permissions" }))) { >+ return $c->$cb({error => "You don't have the required permission"}, 403); >+ } >+ >+ my ($can_renew, $error) = C4::Circulation::CanBookBeRenewed( >+ $borrowernumber, $itemnumber); >+ >+ if (!$can_renew) { >+ return $c->$cb({error => "Renewal not authorized ($error)"}, 403); >+ } >+ >+ AddRenewal($borrowernumber, $itemnumber, $checkout->branchcode); >+ $checkout = Koha::Issues->find($checkout_id); >+ >+ return $c->$cb($checkout->unblessed, 200); >+} >+ >+sub listhistory { >+ my ($c, $args, $cb) = @_; >+ >+ my $user = $c->stash('koha.user'); >+ unless ($user && haspermission($user->userid, { circulate => "circulate_remaining_permissions" })) { >+ return $c->$cb({error => "You don't have the required permission"}, 403); >+ } >+ >+ my $borrowernumber = $c->param('borrowernumber'); >+ >+ my %attributes = ( itemnumber => { "!=", undef } ); >+ if ($borrowernumber) { >+ return $c->$cb({ >+ error => "Patron doesn't exist" >+ }, 404) unless Koha::Patrons->find($borrowernumber); >+ >+ $attributes{borrowernumber} = $borrowernumber; >+ } >+ >+ # Retrieve all the issues in the history, but only the issue_id due to possible perfomance issues >+ my $checkouts = Koha::OldIssues->search( >+ \%attributes, >+ { columns => [qw/issue_id/]} >+ ); >+ >+ $c->$cb($checkouts->unblessed, 200); >+} >+ >+sub gethistory { >+ my ($c, $args, $cb) = @_; >+ >+ my $user = $c->stash('koha.user'); >+ unless ($user && haspermission($user->userid, { circulate => "circulate_remaining_permissions" })) { >+ return $c->$cb({error => "You don't have the required permission"}, 403); >+ } >+ >+ my $checkout_id = $args->{checkout_id}; >+ my $checkout = Koha::OldIssues->find($checkout_id); >+ >+ if (!$checkout) { >+ return $c->$cb({ >+ error => "Checkout doesn't exist" >+ }, 404); >+ } >+ >+ return $c->$cb($checkout->unblessed, 200); >+} >+ >+1; >diff --git a/Koha/REST/V1/Item.pm b/Koha/REST/V1/Item.pm >new file mode 100644 >index 0000000..b51a225 >--- /dev/null >+++ b/Koha/REST/V1/Item.pm >@@ -0,0 +1,54 @@ >+package Koha::REST::V1::Item; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+use Mojo::JSON; >+ >+use C4::Auth qw( haspermission ); >+use C4::Items qw( GetHiddenItemnumbers ); >+ >+use Koha::Items; >+ >+sub get { >+ my ($c, $args, $cb) = @_; >+ >+ my $itemnumber = $c->param('itemnumber'); >+ my $item = Koha::Items->find($itemnumber); >+ unless ($item) { >+ return $c->$cb({error => "Item not found"}, 404); >+ } >+ >+ # Hide non-public itemnotes if user has no staff access >+ my $user = $c->stash('koha.user'); >+ unless ($user && haspermission($user->userid, {catalogue => 1})) { >+ >+ my @hiddenitems = C4::Items::GetHiddenItemnumbers( ({ itemnumber => $itemnumber}) ); >+ my %hiddenitems = map { $_ => 1 } @hiddenitems; >+ >+ # Pretend it was not found as it's hidden from OPAC to regular users >+ return $c->$cb({error => "Item not found"}, 404) >+ if $hiddenitems{$itemnumber}; >+ >+ $item->set({ itemnotes_nonpublic => undef }); >+ } >+ >+ return $c->$cb($item->unblessed, 200); >+} >+ >+1; >diff --git a/Koha/REST/V1/Library.pm b/Koha/REST/V1/Library.pm >new file mode 100644 >index 0000000..30a8982 >--- /dev/null >+++ b/Koha/REST/V1/Library.pm >@@ -0,0 +1,42 @@ >+package Koha::REST::V1::Library; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+use Koha::Libraries; >+ >+sub list { >+ my ($c, $args, $cb) = @_; >+ >+ my $libraries = Koha::Libraries->search; >+ return $c->$cb($libraries->unblessed, 200); >+} >+ >+sub get { >+ my ($c, $args, $cb) = @_; >+ >+ my $brancocode = $c->param('branchcode'); >+ my $library = Koha::Libraries->find({branchcode => $brancocode}); >+ unless ($library) { >+ return $c->$cb({error => "Library with branchcode \"$brancocode\" not found"}, 404); >+ } >+ >+ return $c->$cb($library->unblessed, 200); >+} >+ >+1; >diff --git a/Koha/REST/V1/Patron.pm b/Koha/REST/V1/Patron.pm >index 2851308..0411719 100644 >--- a/Koha/REST/V1/Patron.pm >+++ b/Koha/REST/V1/Patron.pm >@@ -20,7 +20,10 @@ use Modern::Perl; > use Mojo::Base 'Mojolicious::Controller'; > > use C4::Auth qw( haspermission ); >+use Koha::AuthUtils qw(hash_password); > use Koha::Patrons; >+use Koha::Patron::Categories; >+use Koha::Libraries; > > sub list { > my ($c, $args, $cb) = @_; >@@ -30,7 +33,17 @@ sub list { > return $c->$cb({error => "You don't have the required permission"}, 403); > } > >- my $patrons = Koha::Patrons->search; >+ my $params = $c->req->query_params->to_hash; >+ my $patrons; >+ if (keys %$params) { >+ my @valid_params = Koha::Patrons->_resultset->result_source->columns; >+ foreach my $key (keys %$params) { >+ delete $params->{$key} unless grep { $key eq $_ } @valid_params; >+ } >+ $patrons = Koha::Patrons->search($params); >+ } else { >+ $patrons = Koha::Patrons->search; >+ } > > $c->$cb($patrons->unblessed, 200); > } >@@ -55,4 +68,146 @@ sub get { > return $c->$cb($patron->unblessed, 200); > } > >+sub add { >+ my ($c, $args, $cb) = @_; >+ >+ my $user = $c->stash('koha.user'); >+ >+ unless ( $user && haspermission($user->userid, {borrowers => 1}) ) { >+ return $c->$cb({error => "You don't have the required permission"}, 403); >+ } >+ >+ my $body = $c->req->json; >+ >+ # patron cardnumber and/or userid unique? >+ if ($body->{cardnumber} || $body->{userid}) { >+ my $patron = Koha::Patrons->find({cardnumber => $body->{cardnumber}, userid => $body->{userid} }); >+ if ($patron) { >+ return $c->$cb({ >+ error => "Patron cardnumber and userid must be unique", >+ conflict => { cardnumber => $patron->cardnumber, userid => $patron->userid } >+ }, 409); >+ } >+ } >+ >+ my $branch = Koha::Libraries->find({branchcode => $body->{branchcode} }); >+ unless ($branch) { >+ return $c->$cb({error => "Library with branchcode \"" . $body->{branchcode} . "\" does not exist"}, 404); >+ } >+ my $category = Koha::Patron::Categories->find({ categorycode => $body->{categorycode} }); >+ unless ($category) { >+ return $c->$cb({error => "Patron category \"" . $body->{categorycode} . "\" does not exist"}, 404); >+ } >+ # All OK - save new patron >+ >+ if ($body->{password}) { $body->{password} = hash_password($body->{password}) }; # bcrypt password if given >+ >+ my $patron = eval { >+ Koha::Patron->new($body)->store; >+ }; >+ >+ unless ($patron) { >+ return $c->$cb({error => "Something went wrong, check Koha logs for details"}, 500); >+ } >+ >+ return $c->$cb($patron->unblessed, 201); >+} >+ >+sub edit { >+ my ($c, $args, $cb) = @_; >+ >+ my $user = $c->stash('koha.user'); >+ >+ unless ( $user && haspermission($user->userid, {borrowers => 1}) ) { >+ return $c->$cb({error => "You don't have the required permission"}, 403); >+ } >+ >+ my $patron = Koha::Patrons->find($args->{borrowernumber}); >+ >+ unless ($patron) { >+ return $c->$cb({error => "Patron not found"}, 404); >+ } >+ >+ my $body = $c->req->json; >+ >+ # Can we change userid and/or cardnumber? in that case check that they are altered first >+ if ($body->{cardnumber} || $body->{userid}) { >+ if ( ($body->{cardnumber} && $body->{cardnumber} ne $patron->cardnumber) || ($body->{userid} && $body->{userid} ne $patron->userid) ) { >+ my $conflictingPatron = Koha::Patrons->find({cardnumber => $body->{cardnumber}, userid => $body->{userid} }); >+ if ($conflictingPatron) { >+ return $c->$cb({ >+ error => "Patron cardnumber and userid must be unique", >+ conflict => { cardnumber => $conflictingPatron->cardnumber, userid => $conflictingPatron->userid } >+ }, 409); >+ } >+ } >+ } >+ >+ if ($body->{branchcode}) { >+ my $branch = Koha::Libraries->find({branchcode => $body->{branchcode} }); >+ unless ($branch) { >+ return $c->$cb({error => "Library with branchcode \"" . $body->{branchcode} . "\" does not exist"}, 404); >+ } >+ } >+ >+ if ($body->{categorycode}) { >+ my $category = Koha::Patron::Categories->find({ categorycode => $body->{categorycode} }); >+ unless ($category) { >+ return $c->$cb({error => "Patron category \"" . $body->{categorycode} . "\" does not exist"}, 404); >+ } >+ } >+ # ALL OK - Update patron >+ # Perhaps limit/validate what should be updated here? flags, et.al. >+ if ($body->{password}) { $body->{password} = hash_password($body->{password}) }; # bcrypt password if given >+ >+ my $updatedpatron = eval { >+ $patron->set($body); >+ }; >+ >+ if ($updatedpatron) { >+ if ($updatedpatron->is_changed) { >+ >+ my $res = eval { >+ $updatedpatron->store; >+ }; >+ >+ unless ($res) { >+ return $c->$cb({error => "Something went wrong, check Koha logs for details"}, 500); >+ } >+ return $c->$cb($res->unblessed, 200); >+ >+ } else { >+ return $c->$cb({}, 204); # No Content = No changes made >+ } >+ } else { >+ return $c->$cb({error => "Something went wrong, check Koha logs for details"}, 500); >+ } >+} >+ >+sub delete { >+ my ($c, $args, $cb) = @_; >+ my $user = $c->stash('koha.user'); >+ >+ unless ( $user && haspermission($user->userid, {borrowers => 1}) ) { >+ return $c->$cb({error => "You don't have the required permission"}, 403); >+ } >+ >+ my $patron = Koha::Patrons->find($args->{borrowernumber}); >+ >+ unless ($patron) { >+ return $c->$cb({error => "Patron not found"}, 404); >+ } >+ >+ # check if loans, reservations, debarrment, etc. before deletion! >+ my $res = $patron->delete; >+ >+ if ($res eq '1') { >+ return $c->$cb({}, 200); >+ } elsif ($res eq '-1') { >+ return $c->$cb({}, 404); >+ } else { >+ return $c->$cb({}, 400); >+ } >+} >+ > 1; >diff --git a/api/v1/definitions/accountline.json b/api/v1/definitions/accountline.json >new file mode 100644 >index 0000000..d4b612e >--- /dev/null >+++ b/api/v1/definitions/accountline.json >@@ -0,0 +1,56 @@ >+{ >+ "type": "object", >+ "properties": { >+ "accountlines_id": { >+ "description": "Internal account line identifier" >+ }, >+ "borrowernumber": { >+ "description": "Internal borrower identifier" >+ }, >+ "accountno": { >+ "description": "?" >+ }, >+ "itemnumber": { >+ "description": "Internal item identifier" >+ }, >+ "date": { >+ "description": "Date when the account line was created" >+ }, >+ "time": { >+ "description": "Time when the account line was created" >+ }, >+ "amount": { >+ "description": "Amount" >+ }, >+ "description": { >+ "description": "Description of account line" >+ }, >+ "accounttype": { >+ "description": "Type of accountline" >+ }, >+ "amountoutstanding": { >+ "description": "Amount outstanding" >+ }, >+ "lastincrement": { >+ "description": "?" >+ }, >+ "timestamp": { >+ "description": "When the account line was last updated" >+ }, >+ "notify_id": { >+ "description": "?" >+ }, >+ "notify_level": { >+ "description": "?" >+ }, >+ "note": { >+ "description": "Accountline note" >+ }, >+ "manager_id": { >+ "description": "Borrowernumber of user that created the account line" >+ }, >+ "meansofpayment": { >+ "description": "Means of payment" >+ } >+ } >+} >diff --git a/api/v1/definitions/amountpaid.json b/api/v1/definitions/amountpaid.json >new file mode 100644 >index 0000000..589183f >--- /dev/null >+++ b/api/v1/definitions/amountpaid.json >@@ -0,0 +1,9 @@ >+{ >+ "type": "object", >+ "properties": { >+ "amount": { >+ "type": "number", >+ "description": "Amount paid" >+ } >+ } >+} >diff --git a/api/v1/definitions/availabilities.json b/api/v1/definitions/availabilities.json >new file mode 100644 >index 0000000..20fa958 >--- /dev/null >+++ b/api/v1/definitions/availabilities.json >@@ -0,0 +1,4 @@ >+{ >+ "type": "array", >+ "items": { "$ref": "availability.json" } >+} >diff --git a/api/v1/definitions/availability.json b/api/v1/definitions/availability.json >new file mode 100644 >index 0000000..ae70624 >--- /dev/null >+++ b/api/v1/definitions/availability.json >@@ -0,0 +1,57 @@ >+{ >+ "type": "object", >+ "properties": { >+ "barcode": { >+ "type": ["string", "null"], >+ "description": "item barcode" >+ }, >+ "biblioitemnumber": { >+ "type": "string", >+ "description": "internally assigned biblio item identifier" >+ }, >+ "biblionumber": { >+ "type": "string", >+ "description": "internally assigned biblio identifier" >+ }, >+ "checkout": { >+ "$ref": "availabilitystatus.json" >+ }, >+ "expected_available": { >+ "type": ["string", "null"], >+ "description": "date this item is expected to be available" >+ }, >+ "hold": { >+ "$ref": "availabilitystatus.json" >+ }, >+ "holdQueueLength": { >+ "type": ["integer", "null"], >+ "description": "number of holdings placed on title/item" >+ }, >+ "holdingbranch": { >+ "type": ["string", "null"], >+ "description": "library that is currently in possession item" >+ }, >+ "homebranch": { >+ "type": ["string", "null"], >+ "description": "library that owns this item" >+ }, >+ "itemcallnumber": { >+ "type": ["string", "null"], >+ "description": "call number for this item" >+ }, >+ "itemnumber": { >+ "type": "string", >+ "description": "internally assigned item identifier" >+ }, >+ "local_use": { >+ "$ref": "availabilitystatus.json" >+ }, >+ "location": { >+ "type": ["string", "null"], >+ "description": "authorized value for the shelving location for this item" >+ }, >+ "onsite_checkout": { >+ "$ref": "availabilitystatus.json" >+ } >+ } >+} >diff --git a/api/v1/definitions/availabilitystatus.json b/api/v1/definitions/availabilitystatus.json >new file mode 100644 >index 0000000..2bf1ea6 >--- /dev/null >+++ b/api/v1/definitions/availabilitystatus.json >@@ -0,0 +1,16 @@ >+{ >+ "type": "object", >+ "properties": { >+ "available": { >+ "type": "boolean", >+ "description": "availability status" >+ }, >+ "description": { >+ "type": "array", >+ "items": { >+ "type": ["string", "null"], >+ "description": "more information on availability" >+ } >+ } >+ } >+} >diff --git a/api/v1/definitions/biblio.json b/api/v1/definitions/biblio.json >new file mode 100644 >index 0000000..a3b35b7 >--- /dev/null >+++ b/api/v1/definitions/biblio.json >@@ -0,0 +1,65 @@ >+{ >+ "type": "object", >+ "properties": { >+ "biblionumber": { >+ "type": "string", >+ "description": "internal bibliographic record identifier" >+ }, >+ "frameworkcode": { >+ "type": "string", >+ "description": "foreign key from the biblio_framework table to identify which framework was used in cataloging this record" >+ }, >+ "author": { >+ "type": ["string", "null"], >+ "description": "statement of responsibility from MARC record (100$a in MARC21)" >+ }, >+ "title": { >+ "type": ["string", "null"], >+ "description": "title (without the subtitle) from the MARC record (245$a in MARC21)" >+ }, >+ "untitle": { >+ "type": ["string", "null"], >+ "description": "uniform title (without the subtitle) from the MARC record (240$a in MARC21)" >+ }, >+ "notes": { >+ "type": ["string", "null"], >+ "description": "values from the general notes field in the MARC record (500$a in MARC21) split by bar (|)" >+ }, >+ "serial": { >+ "type": ["string", "null"], >+ "description": "Boolean indicating whether biblio is for a serial" >+ }, >+ "seriestitle": { >+ "type": ["string", "null"], >+ "description": "Title for describing the series" >+ }, >+ "copyrightdate": { >+ "type": ["string", "null"], >+ "description": "publication or copyright date from the MARC record" >+ }, >+ "timestamp": { >+ "type": "string", >+ "description": "date and time this record was last touched" >+ }, >+ "datecreated": { >+ "type": "string", >+ "description": "the date this record was added to Koha" >+ }, >+ "abstract": { >+ "type": ["string", "null"], >+ "description": "summary from the MARC record (520$a in MARC21)" >+ }, >+ "items": { >+ "type": "array", >+ "items": { >+ "type": "object", >+ "properties": { >+ "itemnumber": { >+ "type": "string", >+ "description": "internal item identifier" >+ } >+ } >+ } >+ } >+ } >+} >diff --git a/api/v1/definitions/checkout.json b/api/v1/definitions/checkout.json >new file mode 100644 >index 0000000..24bfec3 >--- /dev/null >+++ b/api/v1/definitions/checkout.json >@@ -0,0 +1,51 @@ >+{ >+ "type": "object", >+ "properties": { >+ "issue_id": { >+ "type": "string", >+ "description": "internally assigned checkout identifier" >+ }, >+ "borrowernumber": { >+ "type": ["string", "null"], >+ "description": "internally assigned user identifier" >+ }, >+ "itemnumber": { >+ "type": ["string", "null"], >+ "description": "internally assigned item identifier" >+ }, >+ "date_due": { >+ "description": "Due date" >+ }, >+ "branchcode": { >+ "type": ["string", "null"], >+ "description": "code of patron's home branch" >+ }, >+ "issuingbranch": { >+ "description": "Code of the branch where issue was made" >+ }, >+ "returndate": { >+ "description": "Date the item was returned" >+ }, >+ "lastreneweddate": { >+ "description": "Date the item was last renewed" >+ }, >+ "return": { >+ "description": "?" >+ }, >+ "renewals": { >+ "description": "Number of renewals" >+ }, >+ "auto_renew": { >+ "description": "Auto renewal" >+ }, >+ "timestamp": { >+ "description": "Last update time" >+ }, >+ "issuedate": { >+ "description": "Date the item was issued" >+ }, >+ "onsite_checkout": { >+ "description": "On site checkout" >+ } >+ } >+} >diff --git a/api/v1/definitions/checkouts.json b/api/v1/definitions/checkouts.json >new file mode 100644 >index 0000000..0e26d72 >--- /dev/null >+++ b/api/v1/definitions/checkouts.json >@@ -0,0 +1,6 @@ >+{ >+ "type": "array", >+ "items": { >+ "$ref": "./checkout.json" >+ } >+} >diff --git a/api/v1/definitions/editAccountlineBody.json b/api/v1/definitions/editAccountlineBody.json >new file mode 100644 >index 0000000..6cb2f2f >--- /dev/null >+++ b/api/v1/definitions/editAccountlineBody.json >@@ -0,0 +1,17 @@ >+{ >+ "type": "object", >+ "properties": { >+ "amount": { >+ "description": "Amount" >+ }, >+ "amountoutstanding": { >+ "description": "Amount outstanding" >+ }, >+ "note": { >+ "description": "Accountline note" >+ }, >+ "meansofpayment": { >+ "description": "Means of payment" >+ } >+ } >+} >diff --git a/api/v1/definitions/index.json b/api/v1/definitions/index.json >index 3f69544..5918207 100644 >--- a/api/v1/definitions/index.json >+++ b/api/v1/definitions/index.json >@@ -1,6 +1,18 @@ > { >+ "availability": { "$ref": "availability.json" }, >+ "availabilities": { "$ref": "availabilities.json" }, >+ "amountpaid": { "$ref": "amountpaid.json" }, >+ "accountline": { "$ref": "accountline.json" }, >+ "editAccountlineBody": { "$ref": "editAccountlineBody.json" }, >+ "partialpayAccountlineBody": { "$ref": "partialpayAccountlineBody.json" }, > "patron": { "$ref": "patron.json" }, >+ "checkouts": { "$ref": "checkouts.json" }, >+ "checkout": { "$ref": "checkout.json" }, > "holds": { "$ref": "holds.json" }, > "hold": { "$ref": "hold.json" }, >+ "libraries": { "$ref": "libraries.json" }, >+ "library": { "$ref": "library.json" }, >+ "item": { "$ref": "item.json" }, >+ "biblio": { "$ref": "biblio.json" }, > "error": { "$ref": "error.json" } > } >diff --git a/api/v1/definitions/item.json b/api/v1/definitions/item.json >new file mode 100644 >index 0000000..9c43f85 >--- /dev/null >+++ b/api/v1/definitions/item.json >@@ -0,0 +1,177 @@ >+{ >+ "type": "object", >+ "properties": { >+ "itemnumber": { >+ "type": "string", >+ "description": "internally assigned item identifier" >+ }, >+ "biblionumber": { >+ "type": "string", >+ "description": "internally assigned biblio identifier" >+ }, >+ "biblioitemnumber": { >+ "type": "string", >+ "description": "internally assigned biblio item identifier" >+ }, >+ "barcode": { >+ "type": ["string", "null"], >+ "description": "item barcode" >+ }, >+ "dateaccessioned": { >+ "type": ["string", "null"], >+ "description": "date the item was acquired or added to Koha" >+ }, >+ "booksellerid": { >+ "type": ["string", "null"], >+ "description": "where the item was purchased" >+ }, >+ "homebranch": { >+ "type": ["string", "null"], >+ "description": "library that owns this item" >+ }, >+ "price": { >+ "type": ["string", "null"], >+ "description": "purchase price" >+ }, >+ "replacementprice": { >+ "type": ["string", "null"], >+ "description": "cost the library charges to replace the item if it has been marked lost" >+ }, >+ "replacementpricedate": { >+ "type": ["string", "null"], >+ "description": "the date the price is effective from" >+ }, >+ "datelastborrowed": { >+ "type": ["string", "null"], >+ "description": "the date the item was last checked out/issued" >+ }, >+ "datelastseen": { >+ "type": ["string", "null"], >+ "description": "the date the item was last see (usually the last time the barcode was scanned or inventory was done)" >+ }, >+ "stack": { >+ "type": ["string", "null"], >+ "description": "?" >+ }, >+ "notforloan": { >+ "type": "string", >+ "description": "authorized value defining why this item is not for loan" >+ }, >+ "damaged": { >+ "type": "string", >+ "description": "authorized value defining this item as damaged" >+ }, >+ "itemlost": { >+ "type": "string", >+ "description": "authorized value defining this item as lost" >+ }, >+ "itemlost_on": { >+ "type": ["string", "null"], >+ "description": "the date and time an item was last marked as lost, NULL if not lost" >+ }, >+ "withdrawn": { >+ "type": "string", >+ "description": "authorized value defining this item as withdrawn" >+ }, >+ "withdrawn_on": { >+ "type": ["string", "null"], >+ "description": "the date and time an item was last marked as withdrawn, NULL if not withdrawn" >+ }, >+ "itemcallnumber": { >+ "type": ["string", "null"], >+ "description": "call number for this item" >+ }, >+ "coded_location_qualifier": { >+ "type": ["string", "null"], >+ "description": "coded location qualifier" >+ }, >+ "issues": { >+ "type": ["string", "null"], >+ "description": "number of times this item has been checked out/issued" >+ }, >+ "renewals": { >+ "type": ["string", "null"], >+ "description": "number of times this item has been renewed" >+ }, >+ "reserves": { >+ "type": ["string", "null"], >+ "description": "number of times this item has been placed on hold/reserved" >+ }, >+ "restricted": { >+ "type": ["string", "null"], >+ "description": "authorized value defining use restrictions for this item" >+ }, >+ "itemnotes": { >+ "type": ["string", "null"], >+ "description": "public notes on this item" >+ }, >+ "itemnotes_nonpublic": { >+ "type": ["string", "null"], >+ "description": "non-public notes on this item" >+ }, >+ "holdingbranch": { >+ "type": ["string", "null"], >+ "description": "library that is currently in possession item" >+ }, >+ "paidfor": { >+ "type": ["string", "null"], >+ "description": "?" >+ }, >+ "timestamp": { >+ "type": "string", >+ "description": "date and time this item was last altered" >+ }, >+ "location": { >+ "type": ["string", "null"], >+ "description": "authorized value for the shelving location for this item" >+ }, >+ "permanent_location": { >+ "type": ["string", "null"], >+ "description": "linked to the CART and PROC temporary locations feature, stores the permanent shelving location" >+ }, >+ "onloan": { >+ "type": ["string", "null"], >+ "description": "defines if item is checked out (NULL for not checked out, and checkout date for checked out)" >+ }, >+ "cn_source": { >+ "type": ["string", "null"], >+ "description": "classification source used on this item" >+ }, >+ "cn_sort": { >+ "type": ["string", "null"], >+ "description": "?" >+ }, >+ "ccode": { >+ "type": ["string", "null"], >+ "description": "authorized value for the collection code associated with this item" >+ }, >+ "materials": { >+ "type": ["string", "null"], >+ "description": "materials specified" >+ }, >+ "uri": { >+ "type": ["string", "null"], >+ "description": "URL for the item" >+ }, >+ "itype": { >+ "type": ["string", "null"], >+ "description": "itemtype defining the type for this item" >+ }, >+ "more_subfields_xml": { >+ "type": ["string", "null"], >+ "description": "additional 952 subfields in XML format" >+ }, >+ "enumchron": { >+ "type": ["string", "null"], >+ "description": "serial enumeration/chronology for the item" >+ }, >+ "copynumber": { >+ "type": ["string", "null"], >+ "description": "copy number" >+ }, >+ "stocknumber": { >+ "type": ["string", "null"], >+ "description": "inventory number" >+ } >+ } >+} >diff --git a/api/v1/definitions/libraries.json b/api/v1/definitions/libraries.json >new file mode 100644 >index 0000000..a79fd34 >--- /dev/null >+++ b/api/v1/definitions/libraries.json >@@ -0,0 +1,4 @@ >+{ >+ "type": "array", >+ "items": { "$ref": "library.json" } >+} >diff --git a/api/v1/definitions/library.json b/api/v1/definitions/library.json >new file mode 100644 >index 0000000..f841899 >--- /dev/null >+++ b/api/v1/definitions/library.json >@@ -0,0 +1,77 @@ >+{ >+ "type": "object", >+ "properties": { >+ "branchcode": { >+ "type": "string", >+ "description": "Internal library identifier" >+ }, >+ "branchname": { >+ "type": "string", >+ "description": "Printable name of library" >+ }, >+ "branchaddress1": { >+ "type": ["string", "null"], >+ "description": "the first address line of the library" >+ }, >+ "branchaddress2": { >+ "type": ["string", "null"], >+ "description": "the second address line of the library" >+ }, >+ "branchaddress3": { >+ "type": ["string", "null"], >+ "description": "the third address line of the library" >+ }, >+ "branchzip": { >+ "type": ["string", "null"], >+ "description": "the zip or postal code of the library" >+ }, >+ "branchcity": { >+ "type": ["string", "null"], >+ "description": "the city or province of the library" >+ }, >+ "branchstate": { >+ "type": ["string", "null"], >+ "description": "the reqional state of the library" >+ }, >+ "branchcountry": { >+ "type": ["string", "null"], >+ "description": "the county of the library" >+ }, >+ "branchphone": { >+ "type": ["string", "null"], >+ "description": "the primary phone of the library" >+ }, >+ "branchfax": { >+ "type": ["string", "null"], >+ "description": "the fax number of the library" >+ }, >+ "branchemail": { >+ "type": ["string", "null"], >+ "description": "the primary email address of the library" >+ }, >+ "branchreplyto": { >+ "type": ["string", "null"], >+ "description": "the email to be used as a Reply-To" >+ }, >+ "branchreturnpath": { >+ "type": ["string", "null"], >+ "description": "the email to be used as Return-Path" >+ }, >+ "branchurl": { >+ "type": ["string", "null"], >+ "description": "the URL for your library or branch's website" >+ }, >+ "branchip": { >+ "type": ["string", "null"], >+ "description": "the IP address for your library or branch" >+ }, >+ "branchnotes": { >+ "type": ["string", "null"], >+ "description": "notes related to your library or branch" >+ }, >+ "opac_info": { >+ "type": ["string", "null"], >+ "description": "HTML that displays in OPAC" >+ } >+ } >+} >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 b3f30f8..cdd4bc3 100644 >--- a/api/v1/swagger.json >+++ b/api/v1/swagger.json >@@ -14,6 +14,34 @@ > }, > "basePath": "/api/v1", > "paths": { >+ "/availability/items": { >+ "get": { >+ "operationId": "itemsAvailability", >+ "tags": ["items", "availability"], >+ "parameters": [ >+ { "$ref": "#/parameters/itemnumbersQueryParam" }, >+ { "$ref": "#/parameters/biblionumbersQueryParam" } >+ ], >+ "consumes": ["application/json"], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "Availability information on item(s)", >+ "schema": { >+ "$ref": "#/definitions/availabilities" >+ } >+ }, >+ "400": { >+ "description": "Missing or wrong parameters", >+ "schema": { "$ref": "#/definitions/error" } >+ }, >+ "404": { >+ "description": "No item(s) found", >+ "schema": { "$ref": "#/definitions/error" } >+ } >+ } >+ } >+ }, > "/patrons": { > "get": { > "operationId": "listPatrons", >@@ -38,6 +66,55 @@ > } > } > } >+ }, >+ "post": { >+ "operationId": "addPatron", >+ "tags": ["patrons"], >+ "parameters": [{ >+ "name": "body", >+ "in": "body", >+ "description": "A JSON object containing information about the new patron", >+ "required": true, >+ "schema": { >+ "$ref": "#/definitions/patron" >+ } >+ }], >+ "consumes": ["application/json"], >+ "produces": ["application/json"], >+ "responses": { >+ "201": { >+ "description": "A successfully created patron", >+ "schema": { >+ "items": { >+ "$ref": "#/definitions/patron" >+ } >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ }, >+ "404": { >+ "description": "Resource not found", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ }, >+ "409": { >+ "description": "Conflict in creating resource", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ }, >+ "500": { >+ "description": "Internal error", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ } >+ } > } > }, > "/patrons/{borrowernumber}": { >@@ -72,12 +149,100 @@ > } > } > } >+ }, >+ "put": { >+ "operationId": "editPatron", >+ "tags": ["patrons"], >+ "parameters": [ >+ { "$ref": "#/parameters/borrowernumberPathParam" }, >+ { >+ "name": "body", >+ "in": "body", >+ "description": "A JSON object containing new information about existing patron", >+ "required": true, >+ "schema": { >+ "$ref": "#/definitions/patron" >+ } >+ } >+ ], >+ "consumes": ["application/json"], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "A successfully updated patron", >+ "schema": { >+ "items": { >+ "$ref": "#/definitions/patron" >+ } >+ } >+ }, >+ "204": { >+ "description": "No Content", >+ "schema": { >+ "type": "object" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ }, >+ "404": { >+ "description": "Resource not found", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ }, >+ "409": { >+ "description": "Conflict in updating resource", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ }, >+ "500": { >+ "description": "Internal error", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ } >+ } >+ }, >+ "delete": { >+ "operationId": "deletePatron", >+ "tags": ["patrons"], >+ "parameters": [ >+ { "$ref": "#/parameters/borrowernumberPathParam" } >+ ], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "Patron deleted successfully", >+ "schema": { >+ "type": "object" >+ } >+ }, >+ "400": { >+ "description": "Patron deletion failed", >+ "schema": { "$ref": "#/definitions/error" } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ }, >+ "404": { >+ "description": "Patron not found", >+ "schema": { "$ref": "#/definitions/error" } >+ } >+ } > } > }, > "/holds": { > "get": { > "operationId": "listHolds", >- "tags": ["borrowers", "holds"], >+ "tags": ["patrons", "holds"], > "parameters": [ > { > "name": "reserve_id", >@@ -202,7 +367,7 @@ > }, > "post": { > "operationId": "addHold", >- "tags": ["borrowers", "holds"], >+ "tags": ["patrons", "holds"], > "parameters": [ > { > "name": "body", >@@ -332,12 +497,457 @@ > } > } > } >+ }, >+ "/accountlines": { >+ "get": { >+ "operationId": "listAccountlines", >+ "tags": ["accountlines"], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A list of accountlines", >+ "schema": { >+ "type": "array", >+ "items": { >+ "$ref": "#/definitions/accountline" >+ } >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ } >+ } >+ } >+ }, >+ "/libraries": { >+ "get": { >+ "operationId": "listLibrary", >+ "tags": ["libraries"], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A list of libraries", >+ "schema": { >+ "$ref": "#/definitions/libraries" >+ } >+ } >+ } >+ } >+ }, >+ "/libraries/{branchcode}": { >+ "get": { >+ "operationId": "getLibrary", >+ "tags": ["libraries"], >+ "parameters": [ >+ { "$ref": "#/parameters/branchcodePathParam" } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A library", >+ "schema": { >+ "$ref": "#/definitions/library" >+ } >+ }, >+ "404": { >+ "description": "Library not found", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ } >+ } >+ } >+ }, >+ "/checkouts": { >+ "get": { >+ "operationId": "listCheckouts", >+ "tags": ["patrons", "checkouts"], >+ "parameters": [ >+ { >+ "name": "borrowernumber", >+ "in": "query", >+ "description": "Internal patron identifier", >+ "required": false, >+ "type": "integer" >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A list of checkouts", >+ "schema": { >+ "$ref": "#/definitions/checkouts" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { "$ref": "#/definitions/error" } >+ }, >+ "404": { >+ "description": "Borrower not found", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ } >+ } >+ } >+ }, >+ "/checkouts/{checkout_id}": { >+ "get": { >+ "operationId": "getCheckout", >+ "tags": ["patrons", "checkouts"], >+ "parameters": [ >+ { >+ "name": "checkout_id", >+ "in": "path", >+ "description": "Internal checkout identifier", >+ "required": true, >+ "type": "integer" >+ } >+ ], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "Updated borrower's checkout", >+ "schema": { "$ref": "#/definitions/checkout" } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { "$ref": "#/definitions/error" } >+ }, >+ "404": { >+ "description": "Checkout not found", >+ "schema": { "$ref": "#/definitions/error" } >+ } >+ } >+ }, >+ "put": { >+ "operationId": "renewCheckout", >+ "tags": ["patrons", "checkouts"], >+ "parameters": [ >+ { >+ "name": "checkout_id", >+ "in": "path", >+ "description": "Internal checkout identifier", >+ "required": true, >+ "type": "integer" >+ } >+ ], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "Updated borrower's checkout", >+ "schema": { "$ref": "#/definitions/checkout" } >+ }, >+ "403": { >+ "description": "Cannot renew checkout", >+ "schema": { "$ref": "#/definitions/error" } >+ }, >+ "404": { >+ "description": "Checkout not found", >+ "schema": { "$ref": "#/definitions/error" } >+ } >+ } >+ } >+ }, >+ "/checkouts/history": { >+ "get": { >+ "operationId": "listhistoryCheckouts", >+ "tags": ["patrons", "checkouts"], >+ "parameters": [ >+ { >+ "name": "borrowernumber", >+ "in": "query", >+ "description": "Internal patron identifier", >+ "required": false, >+ "type": "integer" >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A list of checkouts history", >+ "schema": { >+ "$ref": "#/definitions/checkouts" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { "$ref": "#/definitions/error" } >+ }, >+ "404": { >+ "description": "Borrower not found", >+ "schema": { >+ "$ref": "#/definitions/error" >+ } >+ } >+ } >+ } >+ }, >+ "/checkouts/history/{checkout_id}": { >+ "get": { >+ "operationId": "gethistoryCheckout", >+ "tags": ["patrons", "checkouts"], >+ "parameters": [ >+ { >+ "name": "checkout_id", >+ "in": "path", >+ "description": "Internal checkout identifier", >+ "required": true, >+ "type": "integer" >+ } >+ ], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "Got borrower's checkout", >+ "schema": { "$ref": "#/definitions/checkout" } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { "$ref": "#/definitions/error" } >+ }, >+ "404": { >+ "description": "Checkout not found", >+ "schema": { "$ref": "#/definitions/error" } >+ } >+ } >+ } >+ }, >+ "/items/{itemnumber}": { >+ "get": { >+ "operationId": "getItem", >+ "tags": ["items"], >+ "parameters": [ >+ { "$ref": "#/parameters/itemnumberPathParam" } >+ ], >+ "consumes": ["application/json"], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "An item", >+ "schema": { "$ref": "#/definitions/item" } >+ }, >+ "400": { >+ "description": "Missing or wrong parameters", >+ "schema": { "$ref": "#/definitions/error" } >+ }, >+ "404": { >+ "description": "Item not found", >+ "schema": { "$ref": "#/definitions/error" } >+ } >+ } >+ } >+ }, >+ "/biblios/{biblionumber}": { >+ "get": { >+ "operationId": "getBiblio", >+ "tags": ["biblios"], >+ "parameters": [ >+ { "$ref": "#/parameters/biblionumberPathParam" } >+ ], >+ "consumes": ["application/json"], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "An biblio", >+ "schema": { "$ref": "#/definitions/biblio" } >+ }, >+ "400": { >+ "description": "Missing or wrong parameters", >+ "schema": { "$ref": "#/definitions/error" } >+ }, >+ "404": { >+ "description": "Biblio not found", >+ "schema": { "$ref": "#/definitions/error" } >+ } >+ } >+ } >+ }, >+ "/accountlines/{accountlines_id}": { >+ "put": { >+ "operationId": "editAccountlines", >+ "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/editAccountlineBody" } >+ } >+ ], >+ "consumes": ["application/json"], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "Updated 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}/payment": { >+ "put": { >+ "operationId": "payAccountlines", >+ "tags": ["accountlines"], >+ "produces": [ >+ "application/json" >+ ], >+ "parameters": [ >+ { "$ref": "#/parameters/accountlinesIdPathParam" } >+ ], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "Paid 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": "Paid 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 paid", >+ "schema": { "$ref": "#/definitions/amountpaid" } >+ }, >+ "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": { > "$ref": "./definitions/index.json" > }, > "parameters": { >+ "biblionumbersQueryParam": { >+ "name": "biblionumber", >+ "in": "query", >+ "description": "Internal biblios identifier", >+ "type": "array", >+ "items": { >+ "type": "integer" >+ }, >+ "collectionFormat": "ssv" >+ }, > "borrowernumberPathParam": { > "name": "borrowernumber", > "in": "path", >@@ -351,6 +961,44 @@ > "description": "Internal hold identifier", > "required": true, > "type": "integer" >+ }, >+ "branchcodePathParam": { >+ "name": "branchcode", >+ "in": "path", >+ "description": "Internal library identifier", >+ "required": true, >+ "type": "string" >+ }, >+ "itemnumberPathParam": { >+ "name": "itemnumber", >+ "in": "path", >+ "description": "Internal item identifier", >+ "required": true, >+ "type": "integer" >+ }, >+ "biblionumberPathParam": { >+ "name": "biblionumber", >+ "in": "path", >+ "description": "Internal biblio identifier", >+ "required": true, >+ "type": "integer" >+ }, >+ "itemnumbersQueryParam": { >+ "name": "itemnumber", >+ "in": "query", >+ "description": "Internal items identifier", >+ "type": "array", >+ "items": { >+ "type": "integer" >+ }, >+ "collectionFormat": "ssv" >+ }, >+ "accountlinesIdPathParam": { >+ "name": "accountlines_id", >+ "in": "path", >+ "description": "Internal accountline identifier", >+ "required": true, >+ "type": "integer" > } > } > } >diff --git a/t/Koha/Item/Availability.t b/t/Koha/Item/Availability.t >new file mode 100644 >index 0000000..5322c19 >--- /dev/null >+++ b/t/Koha/Item/Availability.t >@@ -0,0 +1,71 @@ >+#!/usr/bin/perl >+ >+# Copyright KohaSuomi 2016 >+# >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+use Test::More tests => 16; >+ >+use_ok('Koha::Item::Availability'); >+ >+my $availability = Koha::Item::Availability->new->set_available; >+ >+is($availability->{available}, 1, "Available"); >+$availability->set_needs_confirmation; >+is($availability->{availability_needs_confirmation}, 1, "Needs confirmation"); >+$availability->set_unavailable; >+is($availability->{available}, 0, "Not available"); >+ >+$availability->add_description("such available"); >+$availability->add_description("wow"); >+$availability->add_description("wow"); >+ >+ok($availability->has_description("wow"), "Found description 'wow'"); >+ok($availability->has_description(["wow", "such available"]), >+ "Found description 'wow' and 'such available'"); >+is($availability->has_description(["wow", "much not found"]), 0, >+ "Didn't find 'wow' and 'much not found'"); >+is($availability->{description}[0], "such available", >+ "Found correct description in correct index 1/4"); >+is($availability->{description}[1], "wow", >+ "Found correct description in correct index 2/2"); >+ >+$availability->add_description(["much description", "very doge"]); >+is($availability->{description}[2], "much description", >+ "Found correct description in correct index 3/4"); >+is($availability->{description}[3], "very doge", >+ "Found correct description in correct index 4/4"); >+ >+$availability->del_description("wow"); >+is($availability->{description}[1], "much description", >+ "Found description from correct index after del"); >+$availability->del_description(["very doge", "such available"]); >+is($availability->{description}[0], "much description", >+ "Found description from correct index after del"); >+ >+ >+my $availability_clone = $availability; >+$availability->set_unavailable; >+is($availability_clone->{available}, $availability->{available}, >+ "Availability_clone points to availability"); >+$availability_clone = $availability->clone; >+$availability->set_available; >+isnt($availability_clone->{available}, $availability->{available}, >+ "Availability_clone was cloned and no longer has same availability status"); >+ >+$availability->reset; >+is($availability->{available}, undef, "Availability reset"); >diff --git a/t/db_dependent/Items.t b/t/db_dependent/Items.t >index b494a70..f612995 100755 >--- a/t/db_dependent/Items.t >+++ b/t/db_dependent/Items.t >@@ -20,7 +20,12 @@ use Modern::Perl; > > use MARC::Record; > use C4::Biblio; >+use C4::Circulation; >+use C4::Reserves; > use Koha::Database; >+use Koha::Hold; >+use Koha::Issue; >+use Koha::Item::Availability; > use Koha::Library; > > use t::lib::Mocks; >@@ -432,7 +437,7 @@ subtest 'SearchItems test' => sub { > > subtest 'Koha::Item(s) tests' => sub { > >- plan tests => 5; >+ plan tests => 40; > > $schema->storage->txn_begin(); > >@@ -443,6 +448,15 @@ subtest 'Koha::Item(s) tests' => sub { > my $library2 = $builder->build({ > source => 'Branch', > }); >+ my $borrower = $builder->build({ >+ source => 'Borrower', >+ }); >+ my $itemtype = $builder->build({ >+ source => 'Itemtype', >+ value => { >+ notforloan => 1 >+ } >+ }); > > # Create a biblio and item for testing > t::lib::Mocks::mock_preference('marcflavour', 'MARC21'); >@@ -461,6 +475,101 @@ subtest 'Koha::Item(s) tests' => sub { > is( ref($holdingbranch), 'Koha::Library', "Got Koha::Library from holding_branch method" ); > is( $holdingbranch->branchcode(), $library2->{branchcode}, "Home branch code matches holdingbranch" ); > >+ # Availability tests >+ my $availability = $item->availability_for_checkout(); >+ is (ref($availability), 'Koha::Item::Availability', 'Got Koha::Item::Availability'); >+ is( $availability->{available}, 1, "Item is available" ); >+ $availability = $item->availability_for_local_use(); >+ is( $availability->{available}, 1, "Item is available for local use" ); >+ t::lib::Mocks::mock_preference('OnSiteCheckouts', 0); >+ $availability = $item->availability_for_onsite_checkout(); >+ is( $availability->{available}, 0, "Not available for on-site checkouts" ); >+ is( $availability->{description}[0], "onsite_checkouts_disabled", "Availability description is 'onsite_checkouts_disabled'" ); >+ t::lib::Mocks::mock_preference('OnSiteCheckouts', 1); >+ $availability = $item->availability_for_onsite_checkout(); >+ is( $availability->{available}, 1, "Available for on-site checkouts" ); >+ >+ $item->set({ onloan => "", damaged => 1 })->store(); >+ t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 0); >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 0, "Damaged item unavailable" ); >+ is( $availability->{description}[0], "damaged", "Availability description is 'damaged'" ); >+ $availability = $item->availability_for_local_use(); >+ is( $availability->{available}, 0, "Item is not available for local use" ); >+ $availability = $item->availability_for_onsite_checkout(); >+ is( $availability->{available}, 0, "Item is not available for on-site checkouts" ); >+ t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 1); >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 1, "Damaged item available" ); >+ is( $availability->{description}[0], "damaged", "Availability description is 'damaged'" ); >+ $availability = $item->availability_for_local_use(); >+ is( $availability->{available}, 1, "Item is available for local use" ); >+ $availability = $item->availability_for_onsite_checkout(); >+ is( $availability->{available}, 1, "Item is available for on-site checkouts" ); >+ >+ $item->set({ damaged => 0, withdrawn => 1 })->store(); >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 0, "Item is not available" ); >+ is( $availability->{description}[0], "withdrawn", "Availability description is 'withdrawn'" ); >+ >+ $item->set({ withdrawn => 0, itemlost => 1 })->store(); >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 0, "Item is not available" ); >+ is( $availability->{description}[0], "itemlost", "Availability description is 'itemlost'" ); >+ >+ $item->set({ itemlost => 0, restricted => 1 })->store(); >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 0, "Item is not available" ); >+ is( $availability->{description}[0], "restricted", "Availability description is 'restricted'" ); >+ >+ $item->set({ restricted => 0, notforloan => 1 })->store(); >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 0, "Item is not available" ); >+ is( $availability->{description}[0], "notforloan", "Availability description is 'notforloan'" ); >+ $availability = $item->availability_for_local_use(); >+ is( $availability->{available}, 1, "Item is available for local use" ); >+ $availability = $item->availability_for_onsite_checkout(); >+ is( $availability->{available}, C4::Context->preference('OnSiteCheckouts'), "Good availability for on-site checkouts" ); >+ >+ $item->set({ notforloan => 0, itype => $itemtype->{itemtype} })->store(); >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 0, "Item is not available" ); >+ is( $availability->{description}[0], "notforloan", "Availability description is 'notforloan' (itemtype)" ); >+ $availability = $item->availability_for_local_use(); >+ is( $availability->{available}, 1, "Item is available for local use" ); >+ $availability = $item->availability_for_onsite_checkout(); >+ is( $availability->{available}, C4::Context->preference('OnSiteCheckouts'), "Good availability for on-site checkouts" ); >+ >+ $item->set({ itype => undef, barcode => "test" })->store(); >+ my $reserve = Koha::Hold->new( >+ { >+ biblionumber => $item->biblionumber, >+ itemnumber => $item->itemnumber, >+ waitingdate => '2000-01-01', >+ borrowernumber => $borrower->{borrowernumber}, >+ branchcode => $item->homebranch, >+ suspend => 0, >+ } >+ )->store(); >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 0, "Item is not available" ); >+ is( $availability->{description}[0], "reserved", "Availability description is 'reserved'" ); >+ $availability = $item->availability_for_reserve(); >+ is( $availability->{available}, 1, "Item is available for reserve" ); >+ CancelReserve({ reserve_id => $reserve->reserve_id }); >+ >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 1, "Item is available" ); >+ >+ my $module = new Test::MockModule('C4::Context'); >+ $module->mock( 'userenv', sub { { branch => $borrower->{branchcode} } } ); >+ my $issue = AddIssue($borrower, $item->barcode, undef, 1); >+ $item = Koha::Items->find($item->itemnumber); # refresh item >+ $availability = $item->availability_for_checkout(); >+ is( $availability->{available}, 0, "Item is not available" ); >+ is( $availability->{description}[0], "onloan", "Availability description is 'onloan'" ); >+ is( $availability->{expected_available}, $issue->date_due, "Expected to be available '".$issue->date_due."'"); >+ > $schema->storage->txn_rollback; > }; > >diff --git a/t/db_dependent/api/v1/accountlines.t b/t/db_dependent/api/v1/accountlines.t >new file mode 100644 >index 0000000..d13a447 >--- /dev/null >+++ b/t/db_dependent/api/v1/accountlines.t >@@ -0,0 +1,246 @@ >+#!/usr/bin/env perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 46; >+use Test::Mojo; >+use t::lib::TestBuilder; >+ >+use C4::Auth; >+use C4::Context; >+ >+use Koha::Database; >+ >+my $builder = t::lib::TestBuilder->new(); >+ >+my $dbh = C4::Context->dbh; >+$dbh->{AutoCommit} = 0; >+$dbh->{RaiseError} = 1; >+ >+$ENV{REMOTE_ADDR} = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+my $categorycode = $builder->build({ source => 'Category' })->{ categorycode }; >+my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; >+ >+$t->get_ok('/api/v1/accountlines') >+ ->status_is(403); >+ >+$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 => { >+ branchcode => $branchcode, >+ categorycode => $categorycode, >+ flags => 1024 >+ } >+}); >+ >+my $borrower = $builder->build({ >+ source => 'Borrower', >+ value => { >+ branchcode => $branchcode, >+ categorycode => $categorycode, >+ } >+}); >+ >+my $borrower2 = $builder->build({ >+ source => 'Borrower', >+ value => { >+ branchcode => $branchcode, >+ categorycode => $categorycode, >+ } >+}); >+my $borrowernumber = $borrower->{borrowernumber}; >+my $borrowernumber2 = $borrower2->{borrowernumber}; >+ >+$dbh->do(q| DELETE FROM accountlines |); >+$dbh->do(q| >+ 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(''); >+$session->param('number', $loggedinuser->{ borrowernumber }); >+$session->param('id', $loggedinuser->{ userid }); >+$session->param('ip', '127.0.0.1'); >+$session->param('lasttime', time()); >+$session->flush; >+ >+my $tx = $t->ua->build_tx(GET => "/api/v1/accountlines?borrowernumber=$borrowernumber"); >+$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 $json = $t->tx->res->json; >+ok(ref $json eq 'ARRAY', 'response is a JSON array'); >+ok(scalar @$json == 3, 'response array contains 3 elements'); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/accountlines"); >+$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 = $t->tx->res->json; >+ok(ref $json eq 'ARRAY', 'response is a JSON array'); >+ok(scalar @$json == 4, 'response array contains 3 elements'); >+ >+# Editing accountlines tests >+my $put_data = { >+ 'amount' => -19, >+ 'amountoutstanding' => -19 >+}; >+ >+ >+$tx = $t->ua->build_tx( >+ PUT => "/api/v1/accountlines/11224409" >+ => 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_edit = Koha::Accountlines->search({'borrowernumber' => $borrowernumber2})->unblessed()->[0]; >+ >+$tx = $t->ua->build_tx( >+ PUT => "/api/v1/accountlines/$accountline_to_edit->{accountlines_id}" >+ => 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); >+ >+my $accountline_edited = Koha::Accountlines->search({'borrowernumber' => $borrowernumber2})->unblessed()->[0]; >+ >+is($accountline_edited->{amount}, '-19.000000'); >+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_paid = Koha::Accountlines->search({'borrowernumber' => $borrowernumber, 'amount' => -20})->unblessed()->[0]; >+ok($accountline_paid); >+ >+# 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_partiallypaid = Koha::Accountlines->search({'borrowernumber' => $borrowernumber, 'amount' => 17})->unblessed()->[0]; >+ok($accountline_partiallypaid); >+ >+# 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_partiallypaid = Koha::Accountlines->search({'borrowernumber' => $borrowernumber3, 'amount' => 26})->unblessed()->[0]; >+ >+is($accountline_partiallypaid->{amountoutstanding}, '2.000000'); >+ >+$dbh->rollback; >diff --git a/t/db_dependent/api/v1/availability.t b/t/db_dependent/api/v1/availability.t >new file mode 100644 >index 0000000..b3d18bf >--- /dev/null >+++ b/t/db_dependent/api/v1/availability.t >@@ -0,0 +1,289 @@ >+#!/usr/bin/env perl >+ >+# Copyright KohaSuomi 2016 >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 165; >+use Test::Mojo; >+use t::lib::Mocks; >+use t::lib::TestBuilder; >+ >+use Mojo::JSON; >+ >+use C4::Auth; >+use C4::Circulation; >+use C4::Context; >+ >+use Koha::Database; >+use Koha::Items; >+use Koha::Patron; >+ >+my $builder = t::lib::TestBuilder->new(); >+ >+my $dbh = C4::Context->dbh; >+$dbh->{AutoCommit} = 0; >+$dbh->{RaiseError} = 1; >+ >+$ENV{REMOTE_ADDR} = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+my $categorycode = $builder->build({ source => 'Category' })->{ categorycode }; >+my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; >+ >+my $borrower = $builder->build({ source => 'Borrower' }); >+my $biblio = $builder->build({ source => 'Biblio' }); >+my $biblio2 = $builder->build({ source => 'Biblio' }); >+my $biblionumber = $biblio->{biblionumber}; >+my $biblionumber2 = $biblio2->{biblionumber}; >+ >+my $module = new Test::MockModule('C4::Context'); >+$module->mock( 'userenv', sub { { branch => $borrower->{branchcode} } } ); >+ >+# $item = available, $item2 = unavailable >+my $items; >+$items->{available} = build_item($biblionumber); >+$items->{notforloan} = build_item($biblionumber2, { notforloan => 1 }); >+$items->{damaged} = build_item($biblionumber2, { damaged => 1 }); >+$items->{withdrawn} = build_item($biblionumber2, { withdrawn => 1 }); >+$items->{onloan} = build_item($biblionumber2, { onloan => undef }); >+$items->{itemlost} = build_item($biblionumber2, { itemlost => 1 }); >+$items->{reserved} = build_item($biblionumber2); >+my $reserve = Koha::Hold->new( >+ { >+ biblionumber => $items->{reserved}->{biblionumber}, >+ itemnumber => $items->{reserved}->{itemnumber}, >+ waitingdate => '2000-01-01', >+ borrowernumber => $borrower->{borrowernumber}, >+ branchcode => $items->{reserved}->{homebranch}, >+ suspend => 0, >+ } >+ )->store(); >+ >+my $itemnumber = $items->{available}->{itemnumber}; >+ >+$t->get_ok("/api/v1/availability/items?itemnumber=-500382") >+ ->status_is(404); >+ >+$t->get_ok("/api/v1/availability/items?itemnumber=-500382+-500383") >+ ->status_is(404); >+ >+$t->get_ok("/api/v1/availability/items?biblionumber=-500382") >+ ->status_is(404); >+ >+$t->get_ok("/api/v1/availability/items?biblionumber=-500382+-500383") >+ ->status_is(404); >+ >+t::lib::Mocks::mock_preference('OnSiteCheckouts', 0); >+t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 0); >+# available item >+$t->get_ok("/api/v1/availability/items?itemnumber=$itemnumber") >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $itemnumber) >+ ->json_is('/0/biblionumber', $biblionumber) >+ ->json_is('/0/checkout/available', Mojo::JSON->true) >+ ->json_is('/0/checkout/description', []) >+ ->json_is('/0/hold/available', Mojo::JSON->true) >+ ->json_is('/0/hold/description', []) >+ ->json_is('/0/local_use/available', Mojo::JSON->true) >+ ->json_is('/0/local_use/description', []) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->false) >+ ->json_is('/0/onsite_checkout/description', ["onsite_checkouts_disabled"]) >+ ->json_is('/0/hold_queue_length', 0); >+t::lib::Mocks::mock_preference('OnSiteCheckouts', 1); >+$t->get_ok("/api/v1/availability/items?biblionumber=$biblionumber") >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $itemnumber) >+ ->json_is('/0/biblionumber', $biblionumber) >+ ->json_is('/0/checkout/available', Mojo::JSON->true) >+ ->json_is('/0/checkout/description', []) >+ ->json_is('/0/hold/available', Mojo::JSON->true) >+ ->json_is('/0/hold/description', []) >+ ->json_is('/0/local_use/available', Mojo::JSON->true) >+ ->json_is('/0/local_use/description', []) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->true) >+ ->json_is('/0/onsite_checkout/description', []) >+ ->json_is('/0/hold_queue_length', 0); >+ >+# notforloan item >+$t->get_ok("/api/v1/availability/items?itemnumber=".$items->{notforloan}->{itemnumber}) >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $items->{notforloan}->{itemnumber}) >+ ->json_is('/0/biblionumber', $biblionumber2) >+ ->json_is('/0/checkout/available', Mojo::JSON->false) >+ ->json_is('/0/checkout/description/0', "notforloan") >+ ->json_is('/0/hold/available', Mojo::JSON->false) >+ ->json_is('/0/hold/description', ["notforloan"]) >+ ->json_is('/0/local_use/available', Mojo::JSON->true) >+ ->json_is('/0/local_use/description', []) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->true) >+ ->json_is('/0/onsite_checkout/description', []) >+ ->json_is('/0/hold_queue_length', 0); >+t::lib::Mocks::mock_preference('OnSiteCheckouts', 0); >+$t->get_ok("/api/v1/availability/items?itemnumber=$items->{notforloan}->{itemnumber}") >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $items->{notforloan}->{itemnumber}) >+ ->json_is('/0/biblionumber', $biblionumber2) >+ ->json_is('/0/checkout/available', Mojo::JSON->false) >+ ->json_is('/0/checkout/description', ["notforloan"]) >+ ->json_is('/0/hold/available', Mojo::JSON->false) >+ ->json_is('/0/hold/description', ["notforloan"]) >+ ->json_is('/0/local_use/available', Mojo::JSON->true) >+ ->json_is('/0/local_use/description', []) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->false) >+ ->json_is('/0/onsite_checkout/description', ["onsite_checkouts_disabled"]) >+ ->json_is('/0/hold_queue_length', 0); >+t::lib::Mocks::mock_preference('OnSiteCheckouts', 1); >+ >+# damaged item >+$t->get_ok("/api/v1/availability/items?itemnumber=".$items->{damaged}->{itemnumber}) >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $items->{damaged}->{itemnumber}) >+ ->json_is('/0/biblionumber', $biblionumber2) >+ ->json_is('/0/checkout/available', Mojo::JSON->false) >+ ->json_is('/0/checkout/description', ["damaged"]) >+ ->json_is('/0/hold/available', Mojo::JSON->false) >+ ->json_is('/0/hold/description', ["damaged"]) >+ ->json_is('/0/local_use/available', Mojo::JSON->false) >+ ->json_is('/0/local_use/description', ["damaged"]) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->false) >+ ->json_is('/0/onsite_checkout/description', ["damaged"]) >+ ->json_is('/0/hold_queue_length', 0); >+t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 1); >+$t->get_ok("/api/v1/availability/items?itemnumber=".$items->{damaged}->{itemnumber}) >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $items->{damaged}->{itemnumber}) >+ ->json_is('/0/biblionumber', $biblionumber2) >+ ->json_is('/0/checkout/available', Mojo::JSON->true) >+ ->json_is('/0/checkout/description', ["damaged"]) >+ ->json_is('/0/hold/available', Mojo::JSON->true) >+ ->json_is('/0/hold/description', ["damaged"]) >+ ->json_is('/0/local_use/available', Mojo::JSON->true) >+ ->json_is('/0/local_use/description', ["damaged"]) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->true) >+ ->json_is('/0/onsite_checkout/description', ["damaged"]) >+ ->json_is('/0/hold_queue_length', 0); >+ >+# withdrawn item >+$t->get_ok("/api/v1/availability/items?itemnumber=".$items->{withdrawn}->{itemnumber}) >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $items->{withdrawn}->{itemnumber}) >+ ->json_is('/0/biblionumber', $biblionumber2) >+ ->json_is('/0/checkout/available', Mojo::JSON->false) >+ ->json_is('/0/checkout/description', ["withdrawn"]) >+ ->json_is('/0/hold/available', Mojo::JSON->false) >+ ->json_is('/0/hold/description', ["withdrawn"]) >+ ->json_is('/0/local_use/available', Mojo::JSON->false) >+ ->json_is('/0/local_use/description', ["withdrawn"]) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->false) >+ ->json_is('/0/onsite_checkout/description', ["withdrawn"]) >+ ->json_is('/0/hold_queue_length', 0); >+ >+# lost item >+$t->get_ok("/api/v1/availability/items?itemnumber=".$items->{itemlost}->{itemnumber}) >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $items->{itemlost}->{itemnumber}) >+ ->json_is('/0/biblionumber', $biblionumber2) >+ ->json_is('/0/checkout/available', Mojo::JSON->false) >+ ->json_is('/0/checkout/description', ["itemlost"]) >+ ->json_is('/0/hold/available', Mojo::JSON->false) >+ ->json_is('/0/hold/description', ["itemlost"]) >+ ->json_is('/0/local_use/available', Mojo::JSON->false) >+ ->json_is('/0/local_use/description', ["itemlost"]) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->false) >+ ->json_is('/0/onsite_checkout/description', ["itemlost"]) >+ ->json_is('/0/hold_queue_length', 0); >+ >+my $issue = AddIssue($borrower, $items->{onloan}->{barcode}, undef, 1); >+ >+# issued item >+$t->get_ok("/api/v1/availability/items?itemnumber=".$items->{onloan}->{itemnumber}) >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $items->{onloan}->{itemnumber}) >+ ->json_is('/0/biblionumber', $biblionumber2) >+ ->json_is('/0/checkout/available', Mojo::JSON->false) >+ ->json_is('/0/checkout/description', ["onloan"]) >+ ->json_is('/0/hold/available', Mojo::JSON->true) >+ ->json_is('/0/hold/description', []) >+ ->json_is('/0/local_use/available', Mojo::JSON->false) >+ ->json_is('/0/local_use/description', ["onloan"]) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->false) >+ ->json_is('/0/onsite_checkout/description', ["onloan"]) >+ ->json_is('/0/checkout/expected_available', $issue->date_due) >+ ->json_is('/0/local_use/expected_available', $issue->date_due) >+ ->json_is('/0/onsite_checkout/expected_available', $issue->date_due) >+ ->json_is('/0/hold_queue_length', 0); >+ >+# reserved item >+$t->get_ok("/api/v1/availability/items?itemnumber=".$items->{reserved}->{itemnumber}) >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $items->{reserved}->{itemnumber}) >+ ->json_is('/0/biblionumber', $biblionumber2) >+ ->json_is('/0/checkout/available', Mojo::JSON->false) >+ ->json_is('/0/checkout/description', ["reserved"]) >+ ->json_is('/0/hold/available', Mojo::JSON->true) >+ ->json_is('/0/hold/description', []) >+ ->json_is('/0/local_use/available', Mojo::JSON->false) >+ ->json_is('/0/local_use/description', ["reserved"]) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->false) >+ ->json_is('/0/onsite_checkout/description', ["reserved"]) >+ ->json_is('/0/hold_queue_length', 1); >+ >+# multiple in one request >+$t->get_ok("/api/v1/availability/items?itemnumber=".$items->{notforloan}->{itemnumber}."+$itemnumber+-500382") >+ ->status_is(200) >+ ->json_is('/0/itemnumber', $items->{notforloan}->{itemnumber}) >+ ->json_is('/0/biblionumber', $biblionumber2) >+ ->json_is('/0/checkout/available', Mojo::JSON->false) >+ ->json_is('/0/checkout/description/0', "notforloan") >+ ->json_is('/0/hold/available', Mojo::JSON->false) >+ ->json_is('/0/hold/description', ["notforloan"]) >+ ->json_is('/0/local_use/available', Mojo::JSON->true) >+ ->json_is('/0/local_use/description', []) >+ ->json_is('/0/onsite_checkout/available', Mojo::JSON->true) >+ ->json_is('/0/onsite_checkout/description', []) >+ ->json_is('/0/hold_queue_length', 0) >+ ->json_is('/1/itemnumber', $itemnumber) >+ ->json_is('/1/biblionumber', $biblionumber) >+ ->json_is('/1/checkout/available', Mojo::JSON->true) >+ ->json_is('/1/checkout/description', []) >+ ->json_is('/1/hold/available', Mojo::JSON->true) >+ ->json_is('/1/hold/description', []) >+ ->json_is('/1/local_use/available', Mojo::JSON->true) >+ ->json_is('/1/local_use/description', []) >+ ->json_is('/1/onsite_checkout/available', Mojo::JSON->true) >+ ->json_is('/1/onsite_checkout/description', []) >+ ->json_is('/1/hold_queue_length', 0); >+ >+sub build_item { >+ my ($biblionumber, $field) = @_; >+ >+ return $builder->build({ >+ source => 'Item', >+ value => { >+ biblionumber => $biblionumber, >+ notforloan => $field->{notforloan} || 0, >+ damaged => $field->{damaged} || 0, >+ withdrawn => $field->{withdrawn} || 0, >+ itemlost => $field->{itemlost} || 0, >+ restricted => $field->{restricted} || undef, >+ onloan => $field->{onloan} || undef, >+ itype => $field->{itype} || undef, >+ } >+ }); >+} >diff --git a/t/db_dependent/api/v1/biblios.t b/t/db_dependent/api/v1/biblios.t >new file mode 100644 >index 0000000..848fb7a >--- /dev/null >+++ b/t/db_dependent/api/v1/biblios.t >@@ -0,0 +1,116 @@ >+#!/usr/bin/env perl >+ >+# Copyright 2016 Koha-Suomi >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 7; >+use Test::Mojo; >+use t::lib::TestBuilder; >+ >+use C4::Auth; >+use C4::Biblio; >+use C4::Context; >+use C4::Items; >+ >+use Koha::Database; >+use Koha::Patron; >+use Koha::Items; >+ >+my $builder = t::lib::TestBuilder->new(); >+ >+my $dbh = C4::Context->dbh; >+$dbh->{AutoCommit} = 0; >+$dbh->{RaiseError} = 1; >+ >+$ENV{REMOTE_ADDR} = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+my $categorycode = $builder->build({ source => 'Category' })->{ categorycode }; >+my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; >+my $borrower = $builder->build({ >+ source => 'Borrower', >+ value => { >+ branchcode => $branchcode, >+ categorycode => $categorycode, >+ flags => 16, >+ } >+}); >+ >+my $librarian = $builder->build({ >+ source => "Borrower", >+ value => { >+ categorycode => $categorycode, >+ branchcode => $branchcode, >+ flags => 4, >+ }, >+}); >+ >+my ($session) = create_session($borrower); >+ >+my $biblio = $builder->build({ >+ source => 'Biblio' >+}); >+my $biblionumber = $biblio->{biblionumber}; >+my $item1 = $builder->build({ >+ source => 'Item', >+ value => { >+ biblionumber => $biblionumber, >+ } >+}); >+my $item2 = $builder->build({ >+ source => 'Item', >+ value => { >+ biblionumber => $biblionumber, >+ } >+}); >+my $item1number = $item1->{itemnumber}; >+my $item2number = $item2->{itemnumber}; >+ >+my $nonExistentBiblionumber = -14362719; >+my $tx = $t->ua->build_tx(GET => "/api/v1/biblios/$nonExistentBiblionumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/biblios/$biblionumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/items/0/itemnumber' => $item1number) >+ ->json_is('/items/1/itemnumber' => $item2number) >+ ->json_is('/biblionumber' => $biblionumber); >+ >+$dbh->rollback; >+ >+sub create_session { >+ my (@borrowers) = @_; >+ >+ my @sessions; >+ foreach $borrower (@borrowers) { >+ my $session = C4::Auth::get_session(''); >+ $session->param('number', $borrower->{borrowernumber}); >+ $session->param('id', $borrower->{userid}); >+ $session->param('ip', '127.0.0.1'); >+ $session->param('lasttime', time()); >+ $session->flush; >+ push @sessions, $session; >+ } >+ >+ return @sessions; >+} >diff --git a/t/db_dependent/api/v1/checkouts.t b/t/db_dependent/api/v1/checkouts.t >new file mode 100644 >index 0000000..4db5a8f >--- /dev/null >+++ b/t/db_dependent/api/v1/checkouts.t >@@ -0,0 +1,222 @@ >+#!/usr/bin/env perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 57; >+use Test::MockModule; >+use Test::Mojo; >+use t::lib::Mocks; >+use t::lib::TestBuilder; >+ >+use DateTime; >+use MARC::Record; >+ >+use C4::Context; >+use C4::Biblio; >+use C4::Circulation; >+use C4::Items; >+ >+use Koha::Database; >+use Koha::Patron; >+ >+my $schema = Koha::Database->schema; >+$schema->storage->txn_begin; >+my $dbh = C4::Context->dbh; >+my $builder = t::lib::TestBuilder->new; >+$dbh->{RaiseError} = 1; >+ >+$ENV{REMOTE_ADDR} = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+$dbh->do('DELETE FROM issues'); >+$dbh->do('DELETE FROM items'); >+$dbh->do('DELETE FROM issuingrules'); >+my $loggedinuser = $builder->build({ source => 'Borrower' }); >+ >+$dbh->do(q{ >+ INSERT INTO user_permissions (borrowernumber, module_bit, code) >+ VALUES (?, 1, 'circulate_remaining_permissions') >+}, undef, $loggedinuser->{borrowernumber}); >+ >+my $session = C4::Auth::get_session(''); >+$session->param('number', $loggedinuser->{ borrowernumber }); >+$session->param('id', $loggedinuser->{ userid }); >+$session->param('ip', '127.0.0.1'); >+$session->param('lasttime', time()); >+$session->flush; >+ >+my $borrower = $builder->build({ source => 'Borrower', value => { flags => 0 } }); >+my $borrowernumber = $borrower->{borrowernumber}; >+my $borrower_session = C4::Auth::get_session(''); >+$borrower_session->param('number', $borrowernumber); >+$borrower_session->param('id', $borrower->{ userid }); >+$borrower_session->param('ip', '127.0.0.1'); >+$borrower_session->param('lasttime', time()); >+$borrower_session->flush; >+ >+my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; >+my $module = new Test::MockModule('C4::Context'); >+$module->mock('userenv', sub { { branch => $branchcode } }); >+ >+my $tx = $t->ua->build_tx(GET => "/api/v1/checkouts?borrowernumber=$borrowernumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is([]); >+ >+my $notexisting_borrowernumber = $borrowernumber + 1; >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts?borrowernumber=$notexisting_borrowernumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is([]); >+ >+my $biblionumber = create_biblio('RESTful Web APIs'); >+my $itemnumber1 = create_item($biblionumber, 'TEST000001'); >+my $itemnumber2 = create_item($biblionumber, 'TEST000002'); >+my $itemnumber3 = create_item($biblionumber, 'TEST000003'); >+ >+my $date_due = DateTime->now->add(weeks => 2); >+my $issue1 = C4::Circulation::AddIssue($borrower, 'TEST000001', $date_due); >+my $date_due1 = Koha::DateUtils::dt_from_string( $issue1->date_due ); >+my $issue2 = C4::Circulation::AddIssue($borrower, 'TEST000002', $date_due); >+my $date_due2 = Koha::DateUtils::dt_from_string( $issue2->date_due ); >+my $issue3 = C4::Circulation::AddIssue($loggedinuser, 'TEST000003', $date_due); >+my $date_due3 = Koha::DateUtils::dt_from_string( $issue3->date_due ); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts?borrowernumber=$borrowernumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/0/borrowernumber' => $borrowernumber) >+ ->json_is('/0/itemnumber' => $itemnumber1) >+ ->json_is('/0/date_due' => $date_due1->ymd . ' ' . $date_due1->hms) >+ ->json_is('/1/borrowernumber' => $borrowernumber) >+ ->json_is('/1/itemnumber' => $itemnumber2) >+ ->json_is('/1/date_due' => $date_due2->ymd . ' ' . $date_due2->hms) >+ ->json_hasnt('/2'); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/".$issue3->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $borrower_session->id}); >+$t->request_ok($tx) >+ ->status_is(403) >+ ->json_is({ error => "You don't have the required permission" }); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts?borrowernumber=".$loggedinuser->{borrowernumber}); >+$tx->req->cookies({name => 'CGISESSID', value => $borrower_session->id}); >+$t->request_ok($tx) >+ ->status_is(403) >+ ->json_is({ error => "You don't have the required permission" }); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts?borrowernumber=$borrowernumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $borrower_session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/0/borrowernumber' => $borrowernumber) >+ ->json_is('/0/itemnumber' => $itemnumber1) >+ ->json_is('/0/date_due' => $date_due1->ymd . ' ' . $date_due1->hms) >+ ->json_is('/1/borrowernumber' => $borrowernumber) >+ ->json_is('/1/itemnumber' => $itemnumber2) >+ ->json_is('/1/date_due' => $date_due2->ymd . ' ' . $date_due2->hms) >+ ->json_hasnt('/2'); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/" . $issue1->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $borrower_session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/borrowernumber' => $borrowernumber) >+ ->json_is('/itemnumber' => $itemnumber1) >+ ->json_is('/date_due' => $date_due1->ymd . ' ' . $date_due1->hms) >+ ->json_hasnt('/1'); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/" . $issue1->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/date_due' => $date_due1->ymd . ' ' . $date_due1->hms); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/" . $issue2->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/date_due' => $date_due2->ymd . ' ' . $date_due2->hms); >+ >+ >+$dbh->do('DELETE FROM issuingrules'); >+$dbh->do(q{ >+ INSERT INTO issuingrules (categorycode, branchcode, itemtype, renewalperiod, renewalsallowed) >+ VALUES (?, ?, ?, ?, ?) >+}, {}, '*', '*', '*', 7, 1); >+ >+my $expected_datedue = DateTime->now->add(days => 14)->set(hour => 23, minute => 59, second => 0); >+$tx = $t->ua->build_tx(PUT => "/api/v1/checkouts/" . $issue1->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/date_due' => $expected_datedue->ymd . ' ' . $expected_datedue->hms); >+ >+$tx = $t->ua->build_tx(PUT => "/api/v1/checkouts/" . $issue3->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $borrower_session->id}); >+$t->request_ok($tx) >+ ->status_is(403) >+ ->json_is({ error => "You don't have the required permission" }); >+ >+t::lib::Mocks::mock_preference( "OpacRenewalAllowed", 0 ); >+$tx = $t->ua->build_tx(PUT => "/api/v1/checkouts/" . $issue2->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $borrower_session->id}); >+$t->request_ok($tx) >+ ->status_is(403) >+ ->json_is({ error => "You don't have the required permission" }); >+ >+t::lib::Mocks::mock_preference( "OpacRenewalAllowed", 1 ); >+$tx = $t->ua->build_tx(PUT => "/api/v1/checkouts/" . $issue2->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $borrower_session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/date_due' => $expected_datedue->ymd . ' ' . $expected_datedue->hms); >+ >+$tx = $t->ua->build_tx(PUT => "/api/v1/checkouts/" . $issue1->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(403) >+ ->json_is({ error => 'Renewal not authorized (too_many)' }); >+ >+sub create_biblio { >+ my ($title) = @_; >+ >+ my $record = new MARC::Record; >+ $record->append_fields( >+ new MARC::Field('200', ' ', ' ', a => $title), >+ ); >+ >+ my ($biblionumber) = C4::Biblio::AddBiblio($record, ''); >+ >+ return $biblionumber; >+} >+ >+sub create_item { >+ my ($biblionumber, $barcode) = @_; >+ >+ my $item = { >+ barcode => $barcode, >+ }; >+ >+ my $itemnumber = C4::Items::AddItem($item, $biblionumber); >+ >+ return $itemnumber; >+} >diff --git a/t/db_dependent/api/v1/checkoutshistory.t b/t/db_dependent/api/v1/checkoutshistory.t >new file mode 100644 >index 0000000..c9d988a >--- /dev/null >+++ b/t/db_dependent/api/v1/checkoutshistory.t >@@ -0,0 +1,160 @@ >+#!/usr/bin/env perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 27; >+use Test::MockModule; >+use Test::Mojo; >+use t::lib::TestBuilder; >+ >+use DateTime; >+use MARC::Record; >+ >+use C4::Context; >+use C4::Biblio; >+use C4::Circulation; >+use C4::Items; >+ >+use Koha::Database; >+use Koha::Patron; >+use Koha::OldIssue; >+use Koha::OldIssues; >+ >+my $schema = Koha::Database->schema; >+$schema->storage->txn_begin; >+my $dbh = C4::Context->dbh; >+my $builder = t::lib::TestBuilder->new; >+$dbh->{RaiseError} = 1; >+ >+$ENV{REMOTE_ADDR} = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+$dbh->do('DELETE FROM issues'); >+$dbh->do('DELETE FROM items'); >+$dbh->do('DELETE FROM issuingrules'); >+my $loggedinuser = $builder->build({ source => 'Borrower' }); >+ >+$dbh->do(q{ >+ INSERT INTO user_permissions (borrowernumber, module_bit, code) >+ VALUES (?, 1, 'circulate_remaining_permissions') >+}, undef, $loggedinuser->{borrowernumber}); >+ >+my $session = C4::Auth::get_session(''); >+$session->param('number', $loggedinuser->{ borrowernumber }); >+$session->param('id', $loggedinuser->{ userid }); >+$session->param('ip', '127.0.0.1'); >+$session->param('lasttime', time()); >+$session->flush; >+ >+my $borrower = $builder->build({ source => 'Borrower' }); >+my $borrowernumber = $borrower->{borrowernumber}; >+ >+my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; >+my $module = new Test::MockModule('C4::Context'); >+$module->mock('userenv', sub { { branch => $branchcode } }); >+ >+my $tx = $t->ua->build_tx(GET => "/api/v1/checkouts/history?borrowernumber=$borrowernumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is([]); >+ >+my $notexisting_borrowernumber = $borrowernumber + 1; >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/history?borrowernumber=$notexisting_borrowernumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404) >+ ->json_has('/error'); >+ >+my $biblionumber = create_biblio('RESTful Web APIs'); >+my $itemnumber1 = create_item($biblionumber, 'TEST000001'); >+my $itemnumber2 = create_item($biblionumber, 'TEST000002'); >+ >+my $date_due = DateTime->now->add(weeks => 2); >+ >+my $issueId = Koha::OldIssues->count({}) + int rand(150000); >+my $issue1; >+$issue1 = Koha::OldIssue->new({ issue_id => $issueId, borrowernumber => $borrowernumber, itemnumber => $itemnumber1, date_due => $date_due}); >+$issue1->store(); >+ >+my $date_due1 = Koha::DateUtils::dt_from_string( $issue1->date_due ); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/history?borrowernumber=$borrowernumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/0/issue_id' => $issueId) >+ ->json_hasnt('/1') >+ ->json_hasnt('/error'); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/history/" . $issue1->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/borrowernumber' => $borrowernumber) >+ ->json_is('/itemnumber' => $itemnumber1) >+ ->json_is('/date_due' => $date_due1->ymd . ' ' . $date_due1->hms) >+ ->json_hasnt('/error'); >+ >+$issue1->delete(); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/history/" . $issue1->issue_id); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404) >+ ->json_has('/error'); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/history?borrowernumber=$borrowernumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_hasnt('/0') >+ ->json_hasnt('/error'); >+ >+Koha::Patrons->find($borrowernumber)->delete(); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/checkouts/history?borrowernumber=$borrowernumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404) >+ ->json_has('/error'); >+ >+sub create_biblio { >+ my ($title) = @_; >+ >+ my $record = new MARC::Record; >+ $record->append_fields( >+ new MARC::Field('200', ' ', ' ', a => $title), >+ ); >+ >+ my ($biblionumber) = C4::Biblio::AddBiblio($record, ''); >+ >+ return $biblionumber; >+} >+ >+sub create_item { >+ my ($biblionumber, $barcode) = @_; >+ >+ my $item = { >+ barcode => $barcode, >+ }; >+ >+ my $itemnumber = C4::Items::AddItem($item, $biblionumber); >+ >+ return $itemnumber; >+} >diff --git a/t/db_dependent/api/v1/items.t b/t/db_dependent/api/v1/items.t >new file mode 100644 >index 0000000..0536e0f >--- /dev/null >+++ b/t/db_dependent/api/v1/items.t >@@ -0,0 +1,117 @@ >+#!/usr/bin/env perl >+ >+# Copyright 2016 Koha-Suomi >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 12; >+use Test::Mojo; >+use t::lib::TestBuilder; >+ >+use C4::Auth; >+use C4::Biblio; >+use C4::Context; >+use C4::Items; >+ >+use Koha::Database; >+use Koha::Patron; >+use Koha::Items; >+ >+my $builder = t::lib::TestBuilder->new(); >+ >+my $dbh = C4::Context->dbh; >+$dbh->{AutoCommit} = 0; >+$dbh->{RaiseError} = 1; >+ >+$ENV{REMOTE_ADDR} = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+my $categorycode = $builder->build({ source => 'Category' })->{ categorycode }; >+my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; >+my $borrower = $builder->build({ >+ source => 'Borrower', >+ value => { >+ branchcode => $branchcode, >+ categorycode => $categorycode, >+ flags => 16, >+ } >+}); >+ >+my $librarian = $builder->build({ >+ source => "Borrower", >+ value => { >+ categorycode => $categorycode, >+ branchcode => $branchcode, >+ flags => 4, >+ }, >+}); >+ >+my ($session, $session2) = create_session($borrower, $librarian); >+ >+my $biblio = $builder->build({ >+ source => 'Biblio' >+}); >+my $biblionumber = $biblio->{biblionumber}; >+my $item = $builder->build({ >+ source => 'Item', >+ value => { >+ biblionumber => $biblionumber, >+ } >+}); >+my $itemnumber = $item->{itemnumber}; >+ >+my $nonExistentItemnumber = -14362719; >+my $tx = $t->ua->build_tx(GET => "/api/v1/items/$nonExistentItemnumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/items/$itemnumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/itemnumber' => $itemnumber) >+ ->json_is('/biblionumber' => $biblionumber) >+ ->json_is('/itemnotes_nonpublic' => undef); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/items/$itemnumber"); >+$tx->req->cookies({name => 'CGISESSID', value => $session2->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/itemnumber' => $itemnumber) >+ ->json_is('/biblionumber' => $biblionumber) >+ ->json_is('/itemnotes_nonpublic' => $item->{itemnotes_nonpublic}); >+ >+$dbh->rollback; >+ >+sub create_session { >+ my (@borrowers) = @_; >+ >+ my @sessions; >+ foreach $borrower (@borrowers) { >+ my $session = C4::Auth::get_session(''); >+ $session->param('number', $borrower->{borrowernumber}); >+ $session->param('id', $borrower->{userid}); >+ $session->param('ip', '127.0.0.1'); >+ $session->param('lasttime', time()); >+ $session->flush; >+ push @sessions, $session; >+ } >+ >+ return @sessions; >+} >diff --git a/t/db_dependent/api/v1/libraries.t b/t/db_dependent/api/v1/libraries.t >new file mode 100644 >index 0000000..1f8a936 >--- /dev/null >+++ b/t/db_dependent/api/v1/libraries.t >@@ -0,0 +1,63 @@ >+#!/usr/bin/env perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use t::lib::TestBuilder; >+ >+use Test::More tests => 11; >+use Test::Mojo; >+ >+use C4::Auth; >+use C4::Context; >+use Koha::Database; >+ >+BEGIN { >+ use_ok('Koha::Object'); >+ use_ok('Koha::Libraries'); >+} >+ >+my $schema = Koha::Database->schema; >+my $dbh = C4::Context->dbh; >+my $builder = t::lib::TestBuilder->new; >+ >+$ENV{REMOTE_ADDR} = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+$schema->storage->txn_begin; >+ >+my $branch = $builder->build({ source => 'Branch' }); >+ >+my $tx = $t->ua->build_tx(GET => '/api/v1/libraries'); >+#$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); >+$t->request_ok($tx) >+ ->status_is(200); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/libraries/" . $branch->{ branchcode }); >+#$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/branchcode' => $branch->{ branchcode }) >+ ->json_is('/branchname' => $branch->{ branchname }); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/libraries/" . "nonexistent"); >+$t->request_ok($tx) >+ ->status_is(404) >+ ->json_is('/error' => "Library with branchcode \"nonexistent\" not found"); >+ >+$schema->storage->txn_rollback; >diff --git a/t/db_dependent/api/v1/patrons.t b/t/db_dependent/api/v1/patrons.t >index 6bd4e1a..612b191 100644 >--- a/t/db_dependent/api/v1/patrons.t >+++ b/t/db_dependent/api/v1/patrons.t >@@ -17,28 +17,32 @@ > > use Modern::Perl; > >-use Test::More tests => 10; >-use Test::Mojo; > use t::lib::TestBuilder; > >+use Test::More tests => 64; >+use Test::Mojo; >+ > use C4::Auth; > use C4::Context; >- > use Koha::Database; >-use Koha::Patron; > >-my $builder = t::lib::TestBuilder->new(); >+BEGIN { >+ use_ok('Koha::Object'); >+ use_ok('Koha::Patron'); >+} > >-my $dbh = C4::Context->dbh; >-$dbh->{AutoCommit} = 0; >-$dbh->{RaiseError} = 1; >+my $schema = Koha::Database->schema; >+my $dbh = C4::Context->dbh; >+my $builder = t::lib::TestBuilder->new; > > $ENV{REMOTE_ADDR} = '127.0.0.1'; > my $t = Test::Mojo->new('Koha::REST::V1'); > >+$schema->storage->txn_begin; >+ > my $categorycode = $builder->build({ source => 'Category' })->{ categorycode }; > my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; >-my $borrower = $builder->build({ >+my $patron = $builder->build({ > source => 'Borrower', > value => { > branchcode => $branchcode, >@@ -46,10 +50,12 @@ my $borrower = $builder->build({ > } > }); > >+### GET /api/v1/patrons >+ > $t->get_ok('/api/v1/patrons') > ->status_is(403); > >-$t->get_ok("/api/v1/patrons/" . $borrower->{ borrowernumber }) >+$t->get_ok("/api/v1/patrons/" . $patron->{ borrowernumber }) > ->status_is(403); > > my $loggedinuser = $builder->build({ >@@ -73,12 +79,145 @@ $tx->req->cookies({name => 'CGISESSID', value => $session->id}); > $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); > $t->request_ok($tx) > ->status_is(200); >+ok(@{$tx->res->json} >= 1, 'Json response lists all when no params given'); >+ >+$tx = $t->ua->build_tx(GET => "/api/v1/patrons/" . $patron->{ borrowernumber }); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/borrowernumber' => $patron->{ borrowernumber }) >+ ->json_is('/surname' => $patron->{ surname }); >+ >+$tx = $t->ua->build_tx(GET => '/api/v1/patrons' => form => {surname => 'nonexistent'}); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); >+$t->request_ok($tx) >+ ->status_is(200); >+ok(@{$tx->res->json} == 0, "Json response yields no results when params doesn't match"); > >-$tx = $t->ua->build_tx(GET => "/api/v1/patrons/" . $borrower->{ borrowernumber }); >+$tx = $t->ua->build_tx(GET => '/api/v1/patrons' => form => {surname => $patron->{ surname }}); > $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_is('/borrowernumber' => $borrower->{ borrowernumber }) >- ->json_is('/surname' => $borrower->{ surname }); >+ ->json_has($patron); >+ok(@{$tx->res->json} == 1, 'Json response yields expected results when params match'); >+ >+### POST /api/v1/patrons >+ >+my $newpatron = { >+ branchcode => $branchcode, >+ categorycode => $categorycode, >+ surname => "TestUser", >+ cardnumber => "123456", >+ userid => "testuser" >+}; >+ >+$newpatron->{ branchcode } = "nonexistent"; # Test invalid branchcode >+$tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404) >+ ->json_is('/error' => "Library with branchcode \"nonexistent\" does not exist"); >+ >+$newpatron->{ branchcode } = $branchcode; >+$newpatron->{ categorycode } = "nonexistent"; # Test invalid patron category >+$tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404) >+ ->json_is('/error' => "Patron category \"nonexistent\" does not exist"); >+$newpatron->{ categorycode } = $categorycode; >+ >+$newpatron->{ falseproperty } = "Non existent property"; >+$tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(500) >+ ->json_is('/error' => "Something went wrong, check Koha logs for details"); >+ >+delete $newpatron->{ falseproperty }; >+$tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(201, 'Patron created successfully') >+ ->json_has('/borrowernumber', 'got a borrowernumber') >+ ->json_is('/cardnumber', $newpatron->{ cardnumber }) >+ ->json_is('/surname' => $newpatron->{ surname }) >+ ->json_is('/firstname' => $newpatron->{ firstname }); >+$newpatron->{borrowernumber} = $tx->res->json->{borrowernumber}; >+ >+$tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(409) >+ ->json_has('/error', 'Fails when trying to POST duplicate cardnumber or userid') >+ ->json_has('/conflict', { userid => $newpatron->{ userid }, cardnumber => $newpatron->{ cardnumber } }); >+ >+### PUT /api/v1/patrons >+$tx = $t->ua->build_tx(PUT => "/api/v1/patrons/0" => json => {}); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404) >+ ->json_has('/error', 'Fails when trying to PUT nonexistent patron'); >+ >+$tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" . $newpatron->{ borrowernumber } => json => {categorycode => "nonexistent"}); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404) >+ ->json_is('/error' => "Patron category \"nonexistent\" does not exist"); >+ >+$tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" . $newpatron->{ borrowernumber } => json => {branchcode => "nonexistent"}); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404) >+ ->json_is('/error' => "Library with branchcode \"nonexistent\" does not exist"); >+ >+$newpatron->{ falseproperty } = "Non existent property"; >+$tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" . $newpatron->{ borrowernumber } => json => $newpatron); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(500) >+ ->json_is('/error' => "Something went wrong, check Koha logs for details"); >+delete $newpatron->{ falseproperty }; >+ >+$newpatron->{ cardnumber } = $patron-> { cardnumber }; >+$newpatron->{ userid } = $patron-> { userid }; >+$tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" . $newpatron->{ borrowernumber } => json => $newpatron); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(409) >+ ->json_has('/error' => "Fails when trying to update to an existing cardnumber or userid") >+ ->json_has('/conflict', { cardnumber => $patron->{ cardnumber }, userid => $patron->{ userid } }); >+ >+$newpatron->{ cardnumber } = "123456"; >+$newpatron->{ userid } = "testuser"; >+$tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" . $newpatron->{ borrowernumber } => json => $newpatron); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(204, 'No changes - patron NOT updated'); >+ >+$newpatron->{ cardnumber } = "234567"; >+$newpatron->{ userid } = "updatedtestuser"; >+$newpatron->{ surname } = "UpdatedTestUser"; >+ >+$tx = $t->ua->build_tx(PUT => "/api/v1/patrons/" . $newpatron->{ borrowernumber } => json => $newpatron); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200, 'Patron updated successfully') >+ ->json_has($newpatron); >+ >+### DELETE /api/v1/patrons >+ >+$tx = $t->ua->build_tx(DELETE => "/api/v1/patrons/0"); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(404, 'Patron not found'); >+ >+$tx = $t->ua->build_tx(DELETE => "/api/v1/patrons/" . $newpatron->{ borrowernumber }); >+$tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+$t->request_ok($tx) >+ ->status_is(200, 'Patron deleted successfully'); >+ >+$schema->storage->txn_rollback; > >-$dbh->rollback; >-- >2.1.4
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 16652
:
53866
|
53942
|
53943
|
55642
|
55647
|
55651
|
55652