1 # ---------------------------------------------------------------
2 # Copyright (C) 2005 Georgia Public Library Service
3 # Bill Erickson <highfalutin@gmail.com>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 # ---------------------------------------------------------------
17 package OpenILS::Application::Circ::Holds;
18 use base qw/OpenSRF::Application/;
19 use strict; use warnings;
20 use OpenILS::Application::AppUtils;
21 my $apputils = "OpenILS::Application::AppUtils";
23 use OpenSRF::EX qw(:try);
29 __PACKAGE__->register_method(
30 method => "create_hold",
31 api_name => "open-ils.circ.holds.create",
33 Create a new hold for an item. From a permissions perspective,
34 the login session is used as the 'requestor' of the hold.
35 The hold recipient is determined by the 'usr' setting within
38 First we verify the requestion has holds request permissions.
39 Then we verify that the recipient is allowed to make the given hold.
40 If not, we see if the requestor has "override" capabilities. If not,
41 a permission exception is returned. If permissions allow, we cycle
42 through the set of holds objects and create.
44 If the recipient does not have permission to place multiple holds
45 on a single title and said operation is attempted, a permission
50 my( $self, $client, $login_session, @holds) = @_;
52 if(!@holds){return 0;}
53 my( $user, $evt ) = $apputils->check_ses($login_session);
58 if(ref($holds[0]) eq 'ARRAY') {
60 } else { $holds = [ @holds ]; }
62 warn "Iterating over holds requests...\n";
64 for my $hold (@$holds) {
67 my $type = $hold->hold_type;
70 warn "Hold to create: " . Dumper($hold) . "\n";
73 if($user->id ne $hold->usr) {
74 $recipient = $apputils->fetch_user($hold->usr);
75 if(!$recipient) { return OpenILS::Event->new('USER_NOT_FOUND'); }
81 #enforce the fact that the login is the one requesting the hold
82 $hold->requestor($user->id);
86 # see if the requestor even has permission to request
87 if($hold->requestor ne $hold->usr) {
88 $perm = _check_request_holds_perm($user->id, $user->home_ou);
89 if($perm) { return $perm; }
92 $perm = _check_holds_perm($type, $hold->usr, $recipient->home_ou);
94 #if there is a requestor, see if the requestor has override privelages
95 if($hold->requestor ne $hold->usr) {
96 $perm = _check_request_holds_override($user->id, $user->home_ou);
97 if($perm) {return $perm;}
104 #my $session = $apputils->start_db_session();
105 my $session = OpenSRF::AppSession->create("open-ils.storage");
106 my $method = "open-ils.storage.direct.action.hold_request.create";
107 warn "Sending hold request to storage... $method \n";
109 my $req = $session->request( $method, $hold );
111 my $resp = $req->gather(1);
112 $session->disconnect();
113 if(!$resp) { return OpenILS::EX->new("UNKNOWN")->ex(); }
114 # $apputils->commit_db_session($session);
120 # makes sure that a user has permission to place the type of requested hold
121 # returns the Perm exception if not allowed, returns undef if all is well
122 sub _check_holds_perm {
123 my($type, $user_id, $org_id) = @_;
127 if($evt = $apputils->check_perms(
128 $user_id, $org_id, "MR_HOLDS")) {
132 } elsif ($type eq "T") {
133 if($evt = $apputils->check_perms(
134 $user_id, $org_id, "TITLE_HOLDS")) {
138 } elsif($type eq "V") {
139 if($evt = $apputils->check_perms(
140 $user_id, $org_id, "VOLUME_HOLDS")) {
144 } elsif($type eq "C") {
145 if($evt = $apputils->check_perms(
146 $user_id, $org_id, "COPY_HOLDS")) {
154 # tests if the given user is allowed to place holds on another's behalf
155 sub _check_request_holds_perm {
158 if(my $evt = $apputils->check_perms(
159 $user_id, $org_id, "REQUEST_HOLDS")) {
164 sub _check_request_holds_override {
167 if(my $evt = $apputils->check_perms(
168 $user_id, $org_id, "REQUEST_HOLDS_OVERRIDE")) {
174 __PACKAGE__->register_method(
175 method => "retrieve_holds",
176 api_name => "open-ils.circ.holds.retrieve",
178 Retrieves all the holds for the specified user id. The login session
179 is the requestor and if the requestor is different from the user, then
180 the requestor must have VIEW_HOLDS permissions.
185 my($self, $client, $login_session, $user_id) = @_;
187 my $user = $apputils->check_user_session($login_session);
189 if($user->id ne $user_id) {
190 if($apputils->check_user_perms($user->id, $user->home_ou, "VIEW_HOLDS")) {
191 return OpenILS::Perm->new("VIEW_HOLDS");
195 my $session = OpenSRF::AppSession->create("open-ils.storage");
196 my $req = $session->request(
197 "open-ils.storage.direct.action.hold_request.search.atomic",
198 "usr" => $user_id , fulfillment_time => undef, { order_by => "request_time" });
200 my $h = $req->gather(1);
201 $session->disconnect();
206 __PACKAGE__->register_method(
207 method => "cancel_hold",
208 api_name => "open-ils.circ.hold.cancel",
210 Cancels the specified hold. The login session
211 is the requestor and if the requestor is different from the usr field
212 on the hold, the requestor must have CANCEL_HOLDS permissions.
213 the hold may be either the hold object or the hold id
217 my($self, $client, $login_session, $hold) = @_;
219 my $user = $apputils->check_user_session($login_session);
221 my $session = OpenSRF::AppSession->create("open-ils.storage");
224 $hold = $session->request(
225 "open-ils.storage.direct.action.hold_request.retrieve", $hold)->gather(1);
228 if($user->id ne $hold->usr) {
229 if($apputils->check_user_perms($user->id, $user->home_ou, "CANCEL_HOLDS")) {
230 return OpenILS::Perm->new("CANCEL_HOLDS");
235 warn "Cancelling hold: " . Dumper($hold) . "\n";
237 my $req = $session->request(
238 "open-ils.storage.direct.action.hold_request.delete",
240 my $h = $req->gather(1);
242 warn "[$h] returned from hold_request delete\n";
243 $session->disconnect();
248 __PACKAGE__->register_method(
249 method => "update_hold",
250 api_name => "open-ils.circ.hold.update",
252 Updates the specified hold. The login session
253 is the requestor and if the requestor is different from the usr field
254 on the hold, the requestor must have UPDATE_HOLDS permissions.
258 my($self, $client, $login_session, $hold) = @_;
260 my $user = $apputils->check_user_session($login_session);
262 if($user->id ne $hold->usr) {
263 if($apputils->check_user_perms($user->id, $user->home_ou, "UPDATE_HOLDS")) {
264 return OpenILS::Perm->new("UPDATE_HOLDS");
269 warn "Updating hold: " . Dumper($hold) . "\n";
271 my $session = OpenSRF::AppSession->create("open-ils.storage");
272 my $req = $session->request(
273 "open-ils.storage.direct.action.hold_request.update", $hold );
274 my $h = $req->gather(1);
276 warn "[$h] returned from hold_request update\n";
277 $session->disconnect();
282 __PACKAGE__->register_method(
283 method => "retrieve_hold_status",
284 api_name => "open-ils.circ.hold.status.retrieve",
286 Calculates the current status of the hold.
287 the requestor must have VIEW_HOLDS permissions if the hold is for a user
288 other than the requestor.
289 Returns -1 on error (for now)
290 Returns 1 for 'waiting for copy to become available'
291 Returns 2 for 'waiting for copy capture'
292 Returns 3 for 'in transit'
293 Returns 4 for 'arrived'
296 sub retrieve_hold_status {
297 my($self, $client, $login_session, $hold_id) = @_;
299 my $user = $apputils->check_user_session($login_session);
301 my $session = OpenSRF::AppSession->create("open-ils.storage");
303 my $hold = $session->request(
304 "open-ils.storage.direct.action.hold_request.retrieve", $hold_id )->gather(1);
305 return -1 unless $hold; # should be an exception
308 if($user->id ne $hold->usr) {
309 if($apputils->check_user_perms($user->id, $user->home_ou, "VIEW_HOLDS")) {
310 return OpenILS::Perm->new("VIEW_HOLDS");
314 return 1 unless (defined($hold->current_copy));
316 #return 2 unless (defined($hold->capture_time));
318 my $copy = $session->request(
319 "open-ils.storage.direct.asset.copy.retrieve", $hold->current_copy )->gather(1);
320 return 1 unless $copy; # should be an exception
323 warn "Hold Copy in status check: " . Dumper($copy) . "\n\n";
325 return 4 if ($hold->capture_time and $copy->circ_lib eq $hold->pickup_lib);
327 my $transit = _fetch_hold_transit($session, $hold->id);
328 return 4 if(ref($transit) and defined($transit->dest_recv_time) );
330 return 3 if defined($hold->capture_time);
336 sub _fetch_hold_transit {
339 return $session->request(
340 "open-ils.storage.direct.action.hold_transit_copy.search.hold",
341 $holdid )->gather(1);
344 __PACKAGE__->register_method(
345 method => "capture_copy",
346 api_name => "open-ils.circ.hold.capture_copy.barcode",
348 Captures a copy to fulfil a hold
349 Params is login session and copy barcode
350 Optional param is 'flesh'. If set, we also return the
351 relevant copy and title
352 login mus have COPY_CHECKIN permissions (since this is essentially
357 my( $self, $client, $login_session, $barcode, $flesh ) = @_;
359 warn "Capturing copy with barcode $barcode, flesh=$flesh \n";
361 my $user = $apputils->check_user_session($login_session);
363 if($apputils->check_user_perms($user->id, $user->home_ou, "COPY_CHECKIN")) {
364 return OpenILS::Perm->new("COPY_CHECKIN"); }
366 my $session = $apputils->start_db_session();
368 my $copy = $session->request(
369 "open-ils.storage.direct.asset.copy.search.barcode",
370 $barcode )->gather(1);
372 warn "Found copy $copy\n";
374 return OpenILS::EX->new("UNKNOWN_BARCODE")->ex unless $copy;
376 warn "Capturing copy " . $copy->id . "\n";
378 my $hold = _find_local_hold_for_copy($session, $copy, $user);
379 if(!$hold) {return OpenILS::EX->new("NO_HOLD_FOUND")->ex;}
381 warn "Found hold " . $hold->id . "\n";
383 $hold->current_copy($copy->id);
384 $hold->capture_time("now");
387 my $stat = $session->request(
388 "open-ils.storage.direct.action.hold_request.update", $hold)->gather(1);
389 if(!$stat) { throw OpenSRF::EX ("Error updating hold request " . $copy->id); }
391 $copy->status(8); #status on holds shelf
393 # if the staff member capturing this item is not at the pickup lib
394 if( $user->home_ou ne $hold->pickup_lib ) {
395 $self->_build_hold_transit( $login_session, $session, $hold, $user, $copy );
398 $copy->editor($user->id);
399 $copy->edit_date("now");
400 $stat = $session->request(
401 "open-ils.storage.direct.asset.copy.update", $copy )->gather(1);
402 if(!$stat) { throw OpenSRF::EX ("Error updating copy " . $copy->id); }
407 $title = $session->request(
408 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
409 $copy->id )->gather(1);
410 my $u = OpenILS::Utils::ModsParser->new();
411 $u->start_mods_batch( $title->marc() );
412 $title = $u->finish_mods_batch();
416 $apputils->commit_db_session($session);
420 route_to => $hold->pickup_lib,
427 sub _build_hold_transit {
428 my( $self, $login_session, $session, $hold, $user, $copy ) = @_;
429 my $trans = Fieldmapper::action::hold_transit_copy->new;
431 $trans->hold($hold->id);
432 $trans->source($user->home_ou);
433 $trans->dest($hold->pickup_lib);
434 $trans->source_send_time("now");
435 $trans->target_copy($copy->id);
436 $trans->copy_status($copy->status);
438 my $meth = $self->method_lookup("open-ils.circ.hold_transit.create");
439 my ($stat) = $meth->run( $login_session, $trans, $session );
440 if(!$stat) { throw OpenSRF::EX ("Error creating new hold transit"); }
441 else { $copy->status(6); } #status in transit
445 sub _find_local_hold_for_copy {
451 # first see if this copy has already been selected to fulfill a hold
452 my $hold = $session->request(
453 "open-ils.storage.direct.action.hold_request.search_where",
454 { current_copy => $copy->id, capture_time => undef } )->gather(1);
456 if($hold) {return $hold;}
458 warn "searching for local hold at org " . $user->home_ou . " and copy " . $copy->id . "\n";
460 my $holdid = $session->request(
461 "open-ils.storage.action.hold_request.nearest_hold",
462 $user->home_ou, $copy->id )->gather(1);
464 if(!$holdid) { return undef; }
466 warn "found hold id $holdid\n";
468 return $session->request(
469 "open-ils.storage.direct.action.hold_request.retrieve", $holdid )->gather(1);
474 __PACKAGE__->register_method(
475 method => "create_hold_transit",
476 api_name => "open-ils.circ.hold_transit.create",
478 Creates a new transit object
481 sub create_hold_transit {
482 my( $self, $client, $login_session, $transit, $session ) = @_;
484 my $user = $apputils->check_user_session($login_session);
485 if($apputils->check_user_perms($user->id, $user->home_ou, "CREATE_TRANSIT")) {
486 return OpenILS::Perm->new("CREATE_TRANSIT");
490 if($session) { $ses = $session; }
491 else { $ses = OpenSRF::AppSession->create("open-ils.storage"); }
493 return $ses->request(
494 "open-ils.storage.direct.action.hold_transit_copy.create", $transit )->gather(1);