]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
more more more... see diffs
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Circ / Holds.pm
1 # ---------------------------------------------------------------
2 # Copyright (C) 2005  Georgia Public Library Service 
3 # Bill Erickson <highfalutin@gmail.com>
4
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.
9
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 # ---------------------------------------------------------------
15
16
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";
22 use OpenILS::EX;
23 use OpenSRF::EX qw(:try);
24 use OpenILS::Perm;
25
26
27
28 __PACKAGE__->register_method(
29         method  => "create_hold",
30         api_name        => "open-ils.circ.holds.create",
31         notes           => <<NOTE);
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
35 the hold object.
36
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.
42
43 If the recipient does not have permission to place multiple holds
44 on a single title and said operation is attempted, a permission
45 exception is returned
46 NOTE
47
48 sub create_hold {
49         my( $self, $client, $login_session, @holds) = @_;
50
51         if(!@holds){return 0;}
52         my $user = $apputils->check_user_session($login_session);
53
54
55         my $holds;
56         if(ref($holds[0]) eq 'ARRAY') {
57                 $holds = $holds[0];
58         } else { $holds = [ @holds ]; }
59
60         warn "Iterating over holds requests...\n";
61
62         for my $hold (@$holds) {
63
64                 if(!$hold){next};
65                 my $type = $hold->hold_type;
66
67                 use Data::Dumper;
68                 warn "Hold to create: " . Dumper($hold) . "\n";
69
70                 my $recipient;
71                 if($user->id ne $hold->usr) {
72
73                 } else {
74                         $recipient = $user;
75                 }
76
77                 #enforce the fact that the login is the one requesting the hold
78                 $hold->requestor($user->id); 
79
80                 my $perm = undef;
81
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; }
86                 }
87
88                 $perm = _check_holds_perm($type, $hold->usr, $recipient->home_ou);
89                 if($perm) { 
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;}
94                         } else {
95                                 return $perm; 
96                         }
97                 }
98
99
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";
104
105                 my $req = $session->request( $method, $hold );
106
107                 my $resp = $req->gather(1);
108                 $session->disconnect();
109                 if(!$resp) { return OpenILS::EX->new("UNKNOWN")->ex(); }
110 #               $apputils->commit_db_session($session);
111         }
112
113         return 1;
114 }
115
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) = @_;
120
121         if($type eq "M") {
122                 if($apputils->check_user_perms($user_id, $org_id, "MR_HOLDS")) {
123                         return OpenILS::Perm->new("MR_HOLDS");
124                 } 
125
126         } elsif ($type eq "T") {
127                 if($apputils->check_user_perms($user_id, $org_id, "TITLE_HOLDS")) {
128                         return OpenILS::Perm->new("TITLE_HOLDS");
129                 }
130
131         } elsif($type eq "V") {
132                 if($apputils->check_user_perms($user_id, $org_id, "VOLUME_HOLDS")) {
133                         return OpenILS::Perm->new("VOLUME_HOLDS");
134                 }
135
136         } elsif($type eq "C") {
137                 if($apputils->check_user_perms($user_id, $org_id, "COPY_HOLDS")) {
138                         return OpenILS::Perm->new("COPY_HOLDS");
139                 }
140         }
141
142         return undef;
143 }
144
145 # tests if the given user is allowed to place holds on another's behalf
146 sub _check_request_holds_perm {
147         my $user_id = shift;
148         my $org_id = shift;
149         if($apputils->check_user_perms($user_id, $org_id, "REQUEST_HOLDS")) {
150                 return OpenILS::Perm->new("REQUEST_HOLDS");
151         }
152 }
153
154 sub _check_request_holds_override {
155         my $user_id = shift;
156         my $org_id = shift;
157         if($apputils->check_user_perms($user_id, $org_id, "REQUEST_HOLDS_OVERRIDE")) {
158                 return OpenILS::Perm->new("REQUEST_HOLDS_OVERRIDE");
159         }
160 }
161
162
163 __PACKAGE__->register_method(
164         method  => "retrieve_holds",
165         api_name        => "open-ils.circ.holds.retrieve",
166         notes           => <<NOTE);
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.
170 NOTE
171
172
173 sub retrieve_holds {
174         my($self, $client, $login_session, $user_id) = @_;
175
176         my $user = $apputils->check_user_session($login_session);
177
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");
181                 }
182         }
183
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" });
188
189         my $h = $req->gather(1);
190         $session->disconnect();
191         return $h;
192 }
193
194
195 __PACKAGE__->register_method(
196         method  => "cancel_hold",
197         api_name        => "open-ils.circ.hold.cancel",
198         notes           => <<"  NOTE");
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
203         NOTE
204
205 sub cancel_hold {
206         my($self, $client, $login_session, $hold) = @_;
207
208         my $user = $apputils->check_user_session($login_session);
209
210         my $session = OpenSRF::AppSession->create("open-ils.storage");
211         
212         if(!ref($hold)) {
213                 $hold = $session->request(
214                         "open-ils.storage.direct.action.hold_request.retrieve", $hold)->gather(1);
215         }
216
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");
220                 }
221         }
222
223         use Data::Dumper;
224         warn "Cancelling hold: " . Dumper($hold) . "\n";
225
226         my $req = $session->request(
227                 "open-ils.storage.direct.action.hold_request.delete",
228                 $hold );
229         my $h = $req->gather(1);
230
231         warn "[$h] returned from hold_request delete\n";
232         $session->disconnect();
233         return $h;
234 }
235
236
237 __PACKAGE__->register_method(
238         method  => "update_hold",
239         api_name        => "open-ils.circ.hold.update",
240         notes           => <<"  NOTE");
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.
244         NOTE
245
246 sub update_hold {
247         my($self, $client, $login_session, $hold) = @_;
248
249         my $user = $apputils->check_user_session($login_session);
250
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");
254                 }
255         }
256
257         use Data::Dumper;
258         warn "Updating hold: " . Dumper($hold) . "\n";
259
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);
264
265         warn "[$h] returned from hold_request update\n";
266         $session->disconnect();
267         return $h;
268 }
269
270
271 __PACKAGE__->register_method(
272         method  => "retrieve_hold_status",
273         api_name        => "open-ils.circ.hold.status.retrieve",
274         notes           => <<"  NOTE");
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'
283         NOTE
284
285 sub retrieve_hold_status {
286         my($self, $client, $login_session, $hold_id) = @_;
287
288         my $user = $apputils->check_user_session($login_session);
289
290         my $session = OpenSRF::AppSession->create("open-ils.storage");
291
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
295
296
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");
300                 }
301         }
302         
303         return 1 unless (defined($hold->current_copy));
304
305         #return 2 unless (defined($hold->capture_time));
306
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
310
311         use Data::Dumper;
312         warn "Hold Copy in status check: " . Dumper($copy) . "\n\n";
313
314         return 4 if ($hold->capture_time and $copy->circ_lib eq $hold->pickup_lib);
315
316         my $transit = _fetch_hold_transit($session, $hold->id);
317         return 4 if(ref($transit) and defined($transit->dest_recv_time) ); 
318
319         return 3 if defined($hold->capture_time);
320
321         return 2;
322 }
323
324
325 sub _fetch_hold_transit {
326         my $session = shift;
327         my $holdid = shift;
328         return $session->request(
329                 "open-ils.storage.direct.action.hold_transit_copy.search.hold",
330                 $holdid )->gather(1);
331 }
332
333 __PACKAGE__->register_method(
334         method  => "capture_copy",
335         api_name        => "open-ils.circ.hold.capture_copy.barcode",
336         notes           => <<"  NOTE");
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
342         copy checkin)
343         NOTE
344
345 sub capture_copy {
346         my( $self, $client, $login_session, $barcode, $flesh ) = @_;
347
348         my $user = $apputils->check_user_session($login_session);
349
350         if($apputils->check_user_perms($user->id, $user->home_ou, "COPY_CHECKIN")) {
351                 return OpenILS::Perm->new("COPY_CHECKIN"); }
352
353         my $session = $apputils->start_db_session();
354
355         my $copy = $session->request(
356                 "open-ils.storage.direct.asset.copy.search.barcode",
357                 $barcode )->gather(1);
358         return OpenILS::EX->new("UNKNOWN_BARCODE")->ex unless $copy;
359
360         warn "Capturing copy " . $copy->id . "\n";
361
362         my $hold = _find_local_hold_for_copy($session, $copy, $user);
363         if(!$hold) {return OpenILS::EX->new("HOLD_NOT_FOUND")->ex;}
364
365         warn "Found hold " . $hold->id . "\n";
366
367         $hold->current_copy($copy->id);
368         $hold->capture_time("now"); 
369
370         #update the hold
371         my $stat = $session->request(
372                         "open-ils.storage.direct.action.hold_request.update", $hold)->gather(1);
373         if(!$stat) { throw OpenSRF::EX ("Error updating hold request " . $copy->id); }
374
375         $copy->status(8); #status on holds shelf
376
377         # if the staff member capturing this item is not at the pickup lib
378         if( $user->home_ou ne $hold->pickup_lib ) {
379                 $self->_build_hold_transit( $login_session, $session, $hold, $user, $copy );
380         }
381
382         $copy->editor($user->id);
383         $copy->edit_date("now");
384         $stat = $session->request(
385                 "open-ils.storage.direct.asset.copy.update", $copy )->gather(1);
386         if(!$stat) { throw OpenSRF::EX ("Error updating copy " . $copy->id); }
387
388         
389         my $title = undef;
390         if($flesh) {
391                 $title = $session->request(
392                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
393                         $copy->id )->gather(1);
394                 my $u = OpenILS::Utils::ModsParser->new();
395                 $u->start_mods_batch( $title->marc() );
396                 $title = $u->finish_mods_batch();
397
398         } 
399
400         $apputils->commit_db_session($session);
401
402         return { 
403                 copy => $copy,
404                 route_to => $hold->pickup_lib,
405                 record => $title,
406         };
407
408 }
409
410 sub _build_hold_transit {
411         my( $self, $login_session, $session, $hold, $user, $copy ) = @_;
412         my $trans = Fieldmapper::action::hold_transit_copy->new;
413         $trans->hold($hold->id);
414         $trans->source($user->home_ou);
415         $trans->dest($hold->pickup_lib);
416         $trans->source_send_time("now");
417         $trans->target_copy($copy->id);
418         my $meth = $self->method_lookup("open-ils.circ.hold_transit.create");
419         my ($stat) = $meth->run( $login_session, $trans, $session );
420         if(!$stat) { throw OpenSRF::EX ("Error creating new hold transit"); }
421         else { $copy->status(6); } #status in transit 
422 }
423
424
425 sub _find_local_hold_for_copy {
426
427         my $session = shift;
428         my $copy = shift;
429         my $user = shift;
430
431         # first see if this copy has already been selected to fulfill a hold
432         my $hold  = $session->request(
433                 "open-ils.storage.direct.action.hold_request.search.current_copy",
434                 $copy->id )->gather(1);
435         if($hold) {return $hold;}
436
437         warn "searching for local hold at org " . $user->home_ou . " and copy " . $copy->id . "\n";
438
439         my $holdid = $session->request(
440                 "open-ils.storage.action.hold_request.nearest_hold",
441                 $user->home_ou, $copy->id )->gather(1);
442
443         if(!$holdid) { return undef; }
444
445         warn "found hold id $holdid\n";
446
447         return $session->request(
448                 "open-ils.storage.direct.action.hold_request.retrieve", $holdid )->gather(1);
449
450 }
451
452
453 __PACKAGE__->register_method(
454         method  => "create_hold_transit",
455         api_name        => "open-ils.circ.hold_transit.create",
456         notes           => <<"  NOTE");
457         Creates a new transit object
458         NOTE
459
460 sub create_hold_transit {
461         my( $self, $client, $login_session, $transit, $session ) = @_;
462
463         my $user = $apputils->check_user_session($login_session);
464         if($apputils->check_user_perms($user->id, $user->home_ou, "CREATE_TRANSIT")) {
465                 return OpenILS::Perm->new("CREATE_TRANSIT");
466         }
467         
468         my $ses;
469         if($session) { $ses = $session; } 
470         else { $ses = OpenSRF::AppSession->create("open-ils.storage"); }
471
472         return $ses->request(
473                 "open-ils.storage.direct.action.hold_transit_copy.create", $transit )->gather(1);
474 }
475
476
477
478
479 1;