Bug 17600: Standardize our EXPORT_OK
[srvgit] / Koha / REST / V1 / Holds.pm
index 047a69b..7541a5c 100644 (file)
@@ -19,15 +19,17 @@ use Modern::Perl;
 
 use Mojo::Base 'Mojolicious::Controller';
 
-use C4::Biblio;
+use Mojo::JSON;
+
 use C4::Reserves;
 
 use Koha::Items;
 use Koha::Patrons;
 use Koha::Holds;
-use Koha::DateUtils;
+use Koha::DateUtils qw( dt_from_string output_pref );
 
-use Try::Tiny;
+use List::MoreUtils qw( any );
+use Try::Tiny qw( catch try );
 
 =head1 API
 
@@ -35,7 +37,7 @@ use Try::Tiny;
 
 =head3 list
 
-Mehtod that handles listing Koha::Hold objects
+Method that handles listing Koha::Hold objects
 
 =cut
 
@@ -48,18 +50,7 @@ sub list {
         return $c->render( status => 200, openapi => $holds );
     }
     catch {
-        if ( blessed $_ && $_->isa('Koha::Exceptions') ) {
-            return $c->render(
-                status  => 500,
-                openapi => { error => "$_" }
-            );
-        }
-        else {
-            return $c->render(
-                status  => 500,
-                openapi => { error => "Something went wrong, check Koha logs for details." }
-            );
-        }
+        $c->unhandled_exception($_);
     };
 }
 
@@ -76,6 +67,7 @@ sub add {
         my $body = $c->validation->param('body');
 
         my $biblio;
+        my $item;
 
         my $biblio_id         = $body->{biblio_id};
         my $pickup_library_id = $body->{pickup_library_id};
@@ -85,6 +77,10 @@ sub add {
         my $expiration_date   = $body->{expiration_date};
         my $notes             = $body->{notes};
         my $hold_date         = $body->{hold_date};
+        my $non_priority      = $body->{non_priority};
+
+        my $overrides = $c->stash('koha.overrides');
+        my $can_override = $overrides->{any} && C4::Context->preference('AllowHoldPolicyOverride');
 
         if(!C4::Context->preference( 'AllowHoldDateInFuture' ) && $hold_date) {
             return $c->render(
@@ -109,7 +105,7 @@ sub add {
             }
         }
         elsif ($item_id) {
-            my $item = Koha::Items->find($item_id);
+            $item = Koha::Items->find($item_id);
 
             unless ($item) {
                 return $c->render(
@@ -138,14 +134,46 @@ sub add {
             );
         }
 
+        my $patron = Koha::Patrons->find( $patron_id );
+        unless ($patron) {
+            return $c->render(
+                status  => 400,
+                openapi => { error => 'patron_id not found' }
+            );
+        }
+
+        # Validate pickup location
+        my $valid_pickup_location;
+        if ($item) {    # item-level hold
+            $valid_pickup_location =
+              any { $_->branchcode eq $pickup_library_id }
+            $item->pickup_locations(
+                { patron => $patron } );
+        }
+        else {
+            $valid_pickup_location =
+              any { $_->branchcode eq $pickup_library_id }
+            $biblio->pickup_locations(
+                { patron => $patron } );
+        }
+
+        return $c->render(
+            status  => 400,
+            openapi => {
+                error => 'The supplied pickup location is not valid'
+            }
+        ) unless $valid_pickup_location || $can_override;
+
         my $can_place_hold
             = $item_id
             ? C4::Reserves::CanItemBeReserved( $patron_id, $item_id )
             : C4::Reserves::CanBookBeReserved( $patron_id, $biblio_id );
 
-        my $can_override = C4::Context->preference('AllowHoldPolicyOverride');
+        if ( $patron->holds->count + 1 > C4::Context->preference('maxreserves') ) {
+            $can_place_hold->{status} = 'tooManyReserves';
+        }
 
-        unless ($can_override || $can_place_hold->{status} eq 'OK' ) {
+        unless ( $can_override || $can_place_hold->{status} eq 'OK' ) {
             return $c->render(
                 status => 403,
                 openapi =>
@@ -173,6 +201,7 @@ sub add {
                 itemnumber       => $item_id,
                 found            => undef,                # TODO: Why not?
                 itemtype         => $item_type,
+                non_priority     => $non_priority,
             }
         );
 
@@ -201,26 +230,10 @@ sub add {
                         openapi => Koha::Holds->new->to_api_mapping->{$broken_fk} . ' not found.'
                     );
                 }
-                else {
-                    return $c->render(
-                        status  => 500,
-                        openapi => { error => "Uncaught exception: $_" }
-                    );
-                }
-            }
-            else {
-                return $c->render(
-                    status  => 500,
-                    openapi => { error => "$_" }
-                );
             }
         }
-        else {
-            return $c->render(
-                status  => 500,
-                openapi => { error => "Something went wrong. check the logs." }
-            );
-        }
+
+        $c->unhandled_exception($_);
     };
 }
 
@@ -233,39 +246,61 @@ Method that handles modifying a Koha::Hold object
 sub edit {
     my $c = shift->openapi->valid_input or return;
 
-    my $hold_id = $c->validation->param('hold_id');
-    my $hold = Koha::Holds->find( $hold_id );
+    return try {
+        my $hold_id = $c->validation->param('hold_id');
+        my $hold = Koha::Holds->find( $hold_id );
 
-    unless ($hold) {
-        return $c->render( status  => 404,
-                           openapi => {error => "Hold not found"} );
-    }
+        unless ($hold) {
+            return $c->render(
+                status  => 404,
+                openapi => { error => "Hold not found" }
+            );
+        }
 
-    my $body = $c->req->json;
+        my $overrides = $c->stash('koha.overrides');
+        my $can_override = $overrides->{any} && C4::Context->preference('AllowHoldPolicyOverride');
 
-    my $pickup_library_id = $body->{pickup_library_id};
-    my $priority          = $body->{priority};
-    my $suspended_until   = $body->{suspended_until};
+        my $body = $c->validation->output->{body};
 
-    if ($suspended_until) {
-        $suspended_until = output_pref(dt_from_string($suspended_until, 'rfc3339'));
-    }
+        my $pickup_library_id = $body->{pickup_library_id};
 
-    my $params = {
-        reserve_id    => $hold_id,
-        branchcode    => $pickup_library_id,
-        rank          => $priority,
-        suspend_until => $suspended_until,
-        itemnumber    => $hold->itemnumber
-    };
+        if (
+            defined $pickup_library_id
+            && ( !$hold->is_pickup_location_valid({ library_id => $pickup_library_id }) && !$can_override )
+          )
+        {
+            return $c->render(
+                status  => 400,
+                openapi => {
+                    error => 'The supplied pickup location is not valid'
+                }
+            );
+        }
+
+        $pickup_library_id //= $hold->branchcode;
+        my $priority         = $body->{priority} // $hold->priority;
+        # suspended_until can also be set to undef
+        my $suspended_until   = exists $body->{suspended_until} ? $body->{suspended_until} : $hold->suspend_until;
 
-    C4::Reserves::ModReserve($params);
-    $hold->discard_changes; # refresh
+        my $params = {
+            reserve_id    => $hold_id,
+            branchcode    => $pickup_library_id,
+            rank          => $priority,
+            suspend_until => $suspended_until ? output_pref(dt_from_string($suspended_until, 'rfc3339')) : '',
+            itemnumber    => $hold->itemnumber
+        };
 
-    return $c->render(
-        status  => 200,
-        openapi => $hold->to_api
-    );
+        C4::Reserves::ModReserve($params);
+        $hold->discard_changes; # refresh
+
+        return $c->render(
+            status  => 200,
+            openapi => $hold->to_api
+        );
+    }
+    catch {
+        $c->unhandled_exception($_);
+    };
 }
 
 =head3 delete
@@ -284,9 +319,17 @@ sub delete {
         return $c->render( status => 404, openapi => { error => "Hold not found." } );
     }
 
-    $hold->cancel;
+    return try {
+        $hold->cancel;
 
-    return $c->render( status => 200, openapi => {} );
+        return $c->render(
+            status  => 204,
+            openapi => q{}
+        );
+    }
+    catch {
+        $c->unhandled_exception($_);
+    };
 }
 
 =head3 suspend
@@ -312,15 +355,19 @@ sub suspend {
         $hold->suspend_hold($date);
         $hold->discard_changes;
         $c->res->headers->location( $c->req->url->to_string );
+        my $suspend_end_date;
+        if ($hold->suspend_until) {
+            $suspend_end_date = output_pref({
+                dt         => dt_from_string( $hold->suspend_until ),
+                dateformat => 'rfc3339',
+                dateonly   => 1
+                }
+            );
+        }
         return $c->render(
             status  => 201,
             openapi => {
-                end_date => output_pref(
-                    {   dt         => dt_from_string( $hold->suspend_until ),
-                        dateformat => 'rfc3339',
-                        dateonly   => 1
-                    }
-                )
+                end_date => $suspend_end_date
             }
         );
     }
@@ -328,12 +375,8 @@ sub suspend {
         if ( blessed $_ and $_->isa('Koha::Exceptions::Hold::CannotSuspendFound') ) {
             return $c->render( status => 400, openapi => { error => "$_" } );
         }
-        else {
-            return $c->render(
-                status  => 500,
-                openapi => { error => "Something went wrong. check the logs." }
-            );
-        }
+
+        $c->unhandled_exception($_);
     };
 }
 
@@ -359,10 +402,7 @@ sub resume {
         return $c->render( status => 204, openapi => {} );
     }
     catch {
-        return $c->render(
-            status  => 500,
-            openapi => { error => "Something went wrong. check the logs." }
-        );
+        $c->unhandled_exception($_);
     };
 }
 
@@ -397,11 +437,132 @@ sub update_priority {
         return $c->render( status => 200, openapi => $priority );
     }
     catch {
+        $c->unhandled_exception($_);
+    };
+}
+
+=head3 pickup_locations
+
+Method that returns the possible pickup_locations for a given hold
+used for building the dropdown selector
+
+=cut
+
+sub pickup_locations {
+    my $c = shift->openapi->valid_input or return;
+
+    my $hold_id = $c->validation->param('hold_id');
+    my $hold = Koha::Holds->find( $hold_id, { prefetch => [ 'patron' ] } );
+
+    unless ($hold) {
         return $c->render(
-            status  => 500,
-            openapi => { error => "Something went wrong. check the logs." }
+            status  => 404,
+            openapi => { error => "Hold not found" }
         );
+    }
+
+    return try {
+        my $ps_set;
+
+        if ( $hold->itemnumber ) {
+            $ps_set = $hold->item->pickup_locations( { patron => $hold->patron } );
+        }
+        else {
+            $ps_set = $hold->biblio->pickup_locations( { patron => $hold->patron } );
+        }
+
+        my $pickup_locations = $c->objects->search( $ps_set );
+        my @response = ();
+
+        if ( C4::Context->preference('AllowHoldPolicyOverride') ) {
+
+            my $libraries_rs = Koha::Libraries->search( { pickup_location => 1 } );
+            my $libraries    = $c->objects->search($libraries_rs);
+
+            @response = map {
+                my $library = $_;
+                $library->{needs_override} = (
+                    any { $_->{library_id} eq $library->{library_id} }
+                    @{$pickup_locations}
+                  )
+                  ? Mojo::JSON->false
+                  : Mojo::JSON->true;
+                $library;
+            } @{$libraries};
+
+            return $c->render(
+                status  => 200,
+                openapi => \@response
+            );
+        }
+
+        @response = map { $_->{needs_override} = Mojo::JSON->false; $_; } @{$pickup_locations};
+
+        return $c->render(
+            status  => 200,
+            openapi => \@response
+        );
+    }
+    catch {
+        $c->unhandled_exception($_);
     };
 }
 
+=head3 update_pickup_location
+
+Method that handles modifying the pickup location of a Koha::Hold object
+
+=cut
+
+sub update_pickup_location {
+    my $c = shift->openapi->valid_input or return;
+
+    my $hold_id = $c->validation->param('hold_id');
+    my $body    = $c->validation->param('body');
+    my $pickup_library_id = $body->{pickup_library_id};
+
+    my $hold = Koha::Holds->find($hold_id);
+
+    unless ($hold) {
+        return $c->render(
+            status  => 404,
+            openapi => { error => "Hold not found" }
+        );
+    }
+
+    return try {
+
+        my $overrides    = $c->stash('koha.overrides');
+        my $can_override = $overrides->{any} && C4::Context->preference('AllowHoldPolicyOverride');
+
+        $hold->set_pickup_location(
+            {
+                library_id => $pickup_library_id,
+                force      => $can_override
+            }
+        );
+
+        return $c->render(
+            status  => 200,
+            openapi => {
+                pickup_library_id => $pickup_library_id
+            }
+        );
+    }
+    catch {
+
+        if ( blessed $_ and $_->isa('Koha::Exceptions::Hold::InvalidPickupLocation') ) {
+            return $c->render(
+                status  => 400,
+                openapi => {
+                    error => "$_"
+                }
+            );
+        }
+
+        $c->unhandled_exception($_);
+    };
+}
+
+
 1;