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);
28 __PACKAGE__->register_method(
29 method => "create_hold",
30 api_name => "open-ils.circ.holds.create",
32 Create a new hold for an item. From a permissions perspective,
33 the login session is used as the 'requestor' of the hold.
34 The hold recipient is determined by the 'usr' setting within
37 First we verify the requestion has holds request permissions.
38 Then we verify that the recipient is allowed to make the given hold.
39 If not, we see if the requestor has "override" capabilities. If not,
40 a permission exception is returned. If permissions allow, we cycle
41 through the set of holds objects and create.
43 If the recipient does not have permission to place multiple holds
44 on a single title and said operation is attempted, a permission
49 my( $self, $client, $login_session, @holds) = @_;
51 if(!@holds){return 0;}
52 my $user = $apputils->check_user_session($login_session);
56 if(ref($holds[0]) eq 'ARRAY') {
58 } else { $holds = [ @holds ]; }
60 warn "Iterating over holds requests...\n";
62 for my $hold (@$holds) {
65 my $type = $hold->hold_type;
68 warn "Hold to create: " . Dumper($hold) . "\n";
71 if($user->id ne $hold->usr) {
77 #enforce the fact that the login is the one requesting the hold
78 $hold->requestor($user->id);
82 # see if the requestor even has permission to request
83 if($hold->requestor ne $hold->usr) {
84 $perm = _check_request_holds_perm($type, $user->id, $user->home_ou);
85 if($perm) { return $perm; }
88 $perm = _check_holds_perm($type, $hold->usr, $recipient->home_ou);
90 #if there is a requestor, see if the requestor has override privelages
91 if($hold->requestor ne $hold->usr) {
92 $perm = _check_request_holds_override($user->id, $user->home_ou);
93 if($perm) {return $perm;}
100 #my $session = $apputils->start_db_session();
101 my $session = OpenSRF::AppSession->create("open-ils.storage");
102 my $method = "open-ils.storage.direct.action.hold_request.create";
103 warn "Sending hold request to storage... $method \n";
105 my $req = $session->request( $method, $hold );
107 my $resp = $req->gather(1);
108 $session->disconnect();
109 if(!$resp) { return OpenILS::EX->new("UNKNOWN")->ex(); }
110 # $apputils->commit_db_session($session);
116 # makes sure that a user has permission to place the type of requested hold
117 # returns the Perm exception if not allowed, returns undef if all is well
118 sub _check_holds_perm {
119 my($type, $user_id, $org_id) = @_;
122 if($apputils->check_user_perms($user_id, $org_id, "MR_HOLDS")) {
123 return OpenILS::Perm->new("MR_HOLDS");
126 } elsif ($type eq "T") {
127 if($apputils->check_user_perms($user_id, $org_id, "TITLE_HOLDS")) {
128 return OpenILS::Perm->new("TITLE_HOLDS");
131 } elsif($type eq "V") {
132 if($apputils->check_user_perms($user_id, $org_id, "VOLUME_HOLDS")) {
133 return OpenILS::Perm->new("VOLUME_HOLDS");
136 } elsif($type eq "C") {
137 if($apputils->check_user_perms($user_id, $org_id, "COPY_HOLDS")) {
138 return OpenILS::Perm->new("COPY_HOLDS");
145 # tests if the given user is allowed to place holds on another's behalf
146 sub _check_request_holds_perm {
149 if($apputils->check_user_perms($user_id, $org_id, "REQUEST_HOLDS")) {
150 return OpenILS::Perm->new("REQUEST_HOLDS");
154 sub _check_request_holds_override {
157 if($apputils->check_user_perms($user_id, $org_id, "REQUEST_HOLDS_OVERRIDE")) {
158 return OpenILS::Perm->new("REQUEST_HOLDS_OVERRIDE");
163 __PACKAGE__->register_method(
164 method => "retrieve_holds",
165 api_name => "open-ils.circ.holds.retrieve",
167 Retrieves all the holds for the specified user id. The login session
168 is the requestor and if the requestor is different from the user, then
169 the requestor must have VIEW_HOLDS permissions.
174 my($self, $client, $login_session, $user_id) = @_;
176 my $user = $apputils->check_user_session($login_session);
178 if($user->id ne $user_id) {
179 if($apputils->check_user_perms($user->id, $user->home_ou, "VIEW_HOLDS")) {
180 return OpenILS::Perm->new("VIEW_HOLDS");
184 my $session = OpenSRF::AppSession->create("open-ils.storage");
185 my $req = $session->request(
186 "open-ils.storage.direct.action.hold_request.search.atomic",
187 "usr" => $user_id , fulfillment_time => undef, { order_by => "request_time" });
189 my $h = $req->gather(1);
190 $session->disconnect();
195 __PACKAGE__->register_method(
196 method => "cancel_hold",
197 api_name => "open-ils.circ.hold.cancel",
199 Cancels the specified hold. The login session
200 is the requestor and if the requestor is different from the usr field
201 on the hold, the requestor must have CANCEL_HOLDS permissions.
202 the hold may be either the hold object or the hold id
206 my($self, $client, $login_session, $hold) = @_;
208 my $user = $apputils->check_user_session($login_session);
210 my $session = OpenSRF::AppSession->create("open-ils.storage");
213 $hold = $session->request(
214 "open-ils.storage.direct.action.hold_request.retrieve", $hold)->gather(1);
217 if($user->id ne $hold->usr) {
218 if($apputils->check_user_perms($user->id, $user->home_ou, "CANCEL_HOLDS")) {
219 return OpenILS::Perm->new("CANCEL_HOLDS");
224 warn "Cancelling hold: " . Dumper($hold) . "\n";
226 my $req = $session->request(
227 "open-ils.storage.direct.action.hold_request.delete",
229 my $h = $req->gather(1);
231 warn "[$h] returned from hold_request delete\n";
232 $session->disconnect();
237 __PACKAGE__->register_method(
238 method => "update_hold",
239 api_name => "open-ils.circ.hold.update",
241 Updates the specified hold. The login session
242 is the requestor and if the requestor is different from the usr field
243 on the hold, the requestor must have UPDATE_HOLDS permissions.
247 my($self, $client, $login_session, $hold) = @_;
249 my $user = $apputils->check_user_session($login_session);
251 if($user->id ne $hold->usr) {
252 if($apputils->check_user_perms($user->id, $user->home_ou, "UPDATE_HOLDS")) {
253 return OpenILS::Perm->new("UPDATE_HOLDS");
258 warn "Updating hold: " . Dumper($hold) . "\n";
260 my $session = OpenSRF::AppSession->create("open-ils.storage");
261 my $req = $session->request(
262 "open-ils.storage.direct.action.hold_request.update", $hold );
263 my $h = $req->gather(1);
265 warn "[$h] returned from hold_request update\n";
266 $session->disconnect();
271 __PACKAGE__->register_method(
272 method => "retrieve_hold_status",
273 api_name => "open-ils.circ.hold.status.retrieve",
275 Calculates the current status of the hold.
276 the requestor must have VIEW_HOLDS permissions if the hold is for a user
277 other than the requestor.
278 Returns -1 on error (for now)
279 Returns 1 for 'waiting for copy to become available'
280 Returns 2 for 'waiting for copy capture'
281 Returns 3 for 'in transit'
282 Returns 4 for 'arrived'
285 sub retrieve_hold_status {
286 my($self, $client, $login_session, $hold_id) = @_;
288 my $user = $apputils->check_user_session($login_session);
290 my $session = OpenSRF::AppSession->create("open-ils.storage");
292 my $hold = $session->request(
293 "open-ils.storage.direct.action.hold_request.retrieve", $hold_id )->gather(1);
294 return -1 unless $hold; # should be an exception
297 if($user->id ne $hold->usr) {
298 if($apputils->check_user_perms($user->id, $user->home_ou, "VIEW_HOLDS")) {
299 return OpenILS::Perm->new("VIEW_HOLDS");
303 return 1 unless (defined($hold->current_copy));
305 #return 2 unless (defined($hold->capture_time));
307 my $copy = $session->request(
308 "open-ils.storage.direct.asset.copy.retrieve", $hold->current_copy )->gather(1);
309 return 1 unless $copy; # should be an exception
312 warn "Hold Copy in status check: " . Dumper($copy) . "\n\n";
314 return 4 if ($hold->capture_time and $copy->circ_lib eq $hold->pickup_lib);
316 my $transit = _fetch_hold_transit($session, $hold->id);
317 return 4 if(ref($transit) and defined($transit->dest_recv_time) );
319 return 3 if defined($hold->capture_time);
325 sub _fetch_hold_transit {
328 return $session->request(
329 "open-ils.storage.direct.action.hold_transit_copy.search.hold",
330 $holdid )->gather(1);
333 __PACKAGE__->register_method(
334 method => "capture_copy",
335 api_name => "open-ils.circ.hold.capture_copy.barcode",
337 Captures a copy to fulfil a hold
338 Params is login session and copy barcode
339 Optional param is 'flesh'. If set, we also return the
340 relevant copy and title
341 login mus have COPY_CHECKIN permissions (since this is essentially
346 my( $self, $client, $login_session, $barcode, $flesh ) = @_;
348 warn "Capturing copy with barcode $barcode, flesh=$flesh \n";
350 my $user = $apputils->check_user_session($login_session);
352 if($apputils->check_user_perms($user->id, $user->home_ou, "COPY_CHECKIN")) {
353 return OpenILS::Perm->new("COPY_CHECKIN"); }
355 my $session = $apputils->start_db_session();
357 my $copy = $session->request(
358 "open-ils.storage.direct.asset.copy.search.barcode",
359 $barcode )->gather(1);
361 warn "Found copy $copy\n";
363 return OpenILS::EX->new("UNKNOWN_BARCODE")->ex unless $copy;
365 warn "Capturing copy " . $copy->id . "\n";
367 my $hold = _find_local_hold_for_copy($session, $copy, $user);
368 if(!$hold) {return OpenILS::EX->new("NO_HOLD_FOUND")->ex;}
370 warn "Found hold " . $hold->id . "\n";
372 $hold->current_copy($copy->id);
373 $hold->capture_time("now");
376 my $stat = $session->request(
377 "open-ils.storage.direct.action.hold_request.update", $hold)->gather(1);
378 if(!$stat) { throw OpenSRF::EX ("Error updating hold request " . $copy->id); }
380 $copy->status(8); #status on holds shelf
382 # if the staff member capturing this item is not at the pickup lib
383 if( $user->home_ou ne $hold->pickup_lib ) {
384 $self->_build_hold_transit( $login_session, $session, $hold, $user, $copy );
387 $copy->editor($user->id);
388 $copy->edit_date("now");
389 $stat = $session->request(
390 "open-ils.storage.direct.asset.copy.update", $copy )->gather(1);
391 if(!$stat) { throw OpenSRF::EX ("Error updating copy " . $copy->id); }
396 $title = $session->request(
397 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
398 $copy->id )->gather(1);
399 my $u = OpenILS::Utils::ModsParser->new();
400 $u->start_mods_batch( $title->marc() );
401 $title = $u->finish_mods_batch();
405 $apputils->commit_db_session($session);
409 route_to => $hold->pickup_lib,
415 sub _build_hold_transit {
416 my( $self, $login_session, $session, $hold, $user, $copy ) = @_;
417 my $trans = Fieldmapper::action::hold_transit_copy->new;
418 $trans->hold($hold->id);
419 $trans->source($user->home_ou);
420 $trans->dest($hold->pickup_lib);
421 $trans->source_send_time("now");
422 $trans->target_copy($copy->id);
423 my $meth = $self->method_lookup("open-ils.circ.hold_transit.create");
424 my ($stat) = $meth->run( $login_session, $trans, $session );
425 if(!$stat) { throw OpenSRF::EX ("Error creating new hold transit"); }
426 else { $copy->status(6); } #status in transit
430 sub _find_local_hold_for_copy {
436 # first see if this copy has already been selected to fulfill a hold
437 my $hold = $session->request(
438 "open-ils.storage.direct.action.hold_request.search.current_copy",
439 $copy->id )->gather(1);
440 if($hold) {return $hold;}
442 warn "searching for local hold at org " . $user->home_ou . " and copy " . $copy->id . "\n";
444 my $holdid = $session->request(
445 "open-ils.storage.action.hold_request.nearest_hold",
446 $user->home_ou, $copy->id )->gather(1);
448 if(!$holdid) { return undef; }
450 warn "found hold id $holdid\n";
452 return $session->request(
453 "open-ils.storage.direct.action.hold_request.retrieve", $holdid )->gather(1);
458 __PACKAGE__->register_method(
459 method => "create_hold_transit",
460 api_name => "open-ils.circ.hold_transit.create",
462 Creates a new transit object
465 sub create_hold_transit {
466 my( $self, $client, $login_session, $transit, $session ) = @_;
468 my $user = $apputils->check_user_session($login_session);
469 if($apputils->check_user_perms($user->id, $user->home_ou, "CREATE_TRANSIT")) {
470 return OpenILS::Perm->new("CREATE_TRANSIT");
474 if($session) { $ses = $session; }
475 else { $ses = OpenSRF::AppSession->create("open-ils.storage"); }
477 return $ses->request(
478 "open-ils.storage.direct.action.hold_transit_copy.create", $transit )->gather(1);