]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
9e0bac125ebe63485752c335bbb52b5b8dab3575
[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 OpenILS::Perm;
24
25
26
27 __PACKAGE__->register_method(
28         method  => "create_hold",
29         api_name        => "open-ils.circ.holds.create",
30         notes           => <<NOTE);
31 Create a new hold for an item.  From a permissions perspective, 
32 the login session is used as the 'requestor' of the hold.  
33 The hold recipient is determined by the 'usr' setting within
34 the hold object.
35
36 First we verify the requestion has holds request permissions.
37 Then we verify that the recipient is allowed to make the given hold.
38 If not, we see if the requestor has "override" capabilities.  If not,
39 a permission exception is returned.  If permissions allow, we cycle
40 through the set of holds objects and create.
41
42 If the recipient does not have permission to place multiple holds
43 on a single title and said operation is attempted, a permission
44 exception is returned
45 NOTE
46
47 sub create_hold {
48         my( $self, $client, $login_session, @holds) = @_;
49
50         if(!@holds){return 0;}
51         my $user = $apputils->check_user_session($login_session);
52
53
54         my $holds;
55         if(ref($holds[0]) eq 'ARRAY') {
56                 $holds = $holds[0];
57         } else { $holds = [ @holds ]; }
58
59         warn "Iterating over holds requests...\n";
60
61         for my $hold (@$holds) {
62
63                 if(!$hold){next};
64                 my $type = $hold->hold_type;
65
66                 use Data::Dumper;
67                 warn "Hold to create: " . Dumper($hold) . "\n";
68
69                 my $recipient;
70                 if($user->id ne $hold->usr) {
71
72                 } else {
73                         $recipient = $user;
74                 }
75
76                 #enforce the fact that the login is the one requesting the hold
77                 $hold->requestor($user->id); 
78
79                 my $perm = undef;
80
81                 # see if the requestor even has permission to request
82                 if($hold->requestor ne $hold->usr) {
83                         $perm = _check_request_holds_perm($type, $user->id, $user->home_ou);
84                         if($perm) { return $perm; }
85                 }
86
87                 $perm = _check_holds_perm($type, $hold->usr, $recipient->home_ou);
88                 if($perm) { 
89                         #if there is a requestor, see if the requestor has override privelages
90                         if($hold->requestor ne $hold->usr) {
91                                 $perm = _check_request_holds_override($user->id, $user->home_ou);
92                                 if($perm) {return $perm;}
93                         } else {
94                                 return $perm; 
95                         }
96                 }
97
98
99                 #my $session = $apputils->start_db_session();
100                 my $session = OpenSRF::AppSession->create("open-ils.storage");
101                 my $method = "open-ils.storage.direct.action.hold_request.create";
102                 warn "Sending hold request to storage... $method \n";
103
104                 my $req = $session->request( $method, $hold );
105
106                 my $resp = $req->gather(1);
107                 $session->disconnect();
108                 if(!$resp) { return OpenILS::EX->new("UNKNOWN")->ex(); }
109 #               $apputils->commit_db_session($session);
110         }
111
112         return 1;
113 }
114
115 # makes sure that a user has permission to place the type of requested hold
116 # returns the Perm exception if not allowed, returns undef if all is well
117 sub _check_holds_perm {
118         my($type, $user_id, $org_id) = @_;
119
120         if($type eq "M") {
121                 if($apputils->check_user_perms($user_id, $org_id, "MR_HOLDS")) {
122                         return OpenILS::Perm->new("MR_HOLDS");
123                 } 
124
125         } elsif ($type eq "T") {
126                 if($apputils->check_user_perms($user_id, $org_id, "TITLE_HOLDS")) {
127                         return OpenILS::Perm->new("TITLE_HOLDS");
128                 }
129
130         } elsif($type eq "V") {
131                 if($apputils->check_user_perms($user_id, $org_id, "VOLUME_HOLDS")) {
132                         return OpenILS::Perm->new("VOLUME_HOLDS");
133                 }
134
135         } elsif($type eq "C") {
136                 if($apputils->check_user_perms($user_id, $org_id, "COPY_HOLDS")) {
137                         return OpenILS::Perm->new("COPY_HOLDS");
138                 }
139         }
140
141         return undef;
142 }
143
144 # tests if the given user is allowed to place holds on another's behalf
145 sub _check_request_holds_perm {
146         my $user_id = shift;
147         my $org_id = shift;
148         if($apputils->check_user_perms($user_id, $org_id, "REQUEST_HOLDS")) {
149                 return OpenILS::Perm->new("REQUEST_HOLDS");
150         }
151 }
152
153 sub _check_request_holds_override {
154         my $user_id = shift;
155         my $org_id = shift;
156         if($apputils->check_user_perms($user_id, $org_id, "REQUEST_HOLDS_OVERRIDE")) {
157                 return OpenILS::Perm->new("REQUEST_HOLDS_OVERRIDE");
158         }
159 }
160
161
162 __PACKAGE__->register_method(
163         method  => "retrieve_holds",
164         api_name        => "open-ils.circ.holds.retrieve",
165         notes           => <<NOTE);
166 Retrieves all the holds for the specified user id.  The login session
167 is the requestor and if the requestor is different from the user, then
168 the requestor must have VIEW_HOLDS permissions.
169 NOTE
170
171
172 sub retrieve_holds {
173         my($self, $client, $login_session, $user_id) = @_;
174
175         my $user = $apputils->check_user_session($login_session);
176
177         if($user->id ne $user_id) {
178                 if($apputils->check_user_perms($user->id, $user->home_ou, "VIEW_HOLDS")) {
179                         return OpenILS::Perm->new("VIEW_HOLDS");
180                 }
181         }
182
183         my $session = OpenSRF::AppSession->create("open-ils.storage");
184         my $req = $session->request(
185                 "open-ils.storage.direct.action.hold_request.search.atomic",
186                 "usr" =>  $user_id , { order_by => "request_time" });
187
188         my $h = $req->gather(1);
189         $session->disconnect();
190         return $h;
191 }
192
193
194 __PACKAGE__->register_method(
195         method  => "cancel_hold",
196         api_name        => "open-ils.circ.hold.cancel",
197         notes           => <<"  NOTE");
198         Cancels the specified hold.  The login session
199         is the requestor and if the requestor is different from the usr field
200         on the hold, the requestor must have CANCEL_HOLDS permissions.
201         the hold may be either the hold object or the hold id
202         NOTE
203
204 sub cancel_hold {
205         my($self, $client, $login_session, $hold) = @_;
206
207         my $user = $apputils->check_user_session($login_session);
208
209         my $session = OpenSRF::AppSession->create("open-ils.storage");
210         
211         if(!ref($hold)) {
212                 $hold = $session->request(
213                         "open-ils.storage.direct.action.hold_request.retrieve", $hold)->gather(1);
214         }
215
216         if($user->id ne $hold->usr) {
217                 if($apputils->check_user_perms($user->id, $user->home_ou, "CANCEL_HOLDS")) {
218                         return OpenILS::Perm->new("CANCEL_HOLDS");
219                 }
220         }
221
222         use Data::Dumper;
223         warn "Cancelling hold: " . Dumper($hold) . "\n";
224
225         my $req = $session->request(
226                 "open-ils.storage.direct.action.hold_request.delete",
227                 $hold );
228         my $h = $req->gather(1);
229
230         warn "[$h] returned from hold_request delete\n";
231         $session->disconnect();
232         return $h;
233 }
234
235
236 __PACKAGE__->register_method(
237         method  => "update_hold",
238         api_name        => "open-ils.circ.hold.update",
239         notes           => <<"  NOTE");
240         Updates the specified hold.  The login session
241         is the requestor and if the requestor is different from the usr field
242         on the hold, the requestor must have UPDATE_HOLDS permissions.
243         NOTE
244
245 sub update_hold {
246         my($self, $client, $login_session, $hold) = @_;
247
248         my $user = $apputils->check_user_session($login_session);
249
250         if($user->id ne $hold->usr) {
251                 if($apputils->check_user_perms($user->id, $user->home_ou, "UPDATE_HOLDS")) {
252                         return OpenILS::Perm->new("UPDATE_HOLDS");
253                 }
254         }
255
256         use Data::Dumper;
257         warn "Updating hold: " . Dumper($hold) . "\n";
258
259         my $session = OpenSRF::AppSession->create("open-ils.storage");
260         my $req = $session->request(
261                 "open-ils.storage.direct.action.hold_request.update", $hold );
262         my $h = $req->gather(1);
263
264         warn "[$h] returned from hold_request update\n";
265         $session->disconnect();
266         return $h;
267 }
268
269
270 __PACKAGE__->register_method(
271         method  => "retrieve_hold_status",
272         api_name        => "open-ils.circ.hold.status.retrieve",
273         notes           => <<"  NOTE");
274         Calculates the current status of the hold.
275         the requestor must have VIEW_HOLDS permissions if the hold is for a user
276         other than the requestor.
277         Returns -1  on error (for now)
278         Returns 1 for 'waiting for copy to become available'
279         Returns 2 for 'waiting for copy capture'
280         Returns 3 for 'in transit'
281         Returns 4 for 'arrived'
282         NOTE
283
284 sub retrieve_hold_status {
285         my($self, $client, $login_session, $hold_id) = @_;
286
287         my $user = $apputils->check_user_session($login_session);
288
289         my $session = OpenSRF::AppSession->create("open-ils.storage");
290
291         my $hold = $session->request(
292                 "open-ils.storage.direct.action.hold_request.retrieve", $hold_id )->gather(1);
293         return -1 unless $hold; # should be an exception
294
295
296         if($user->id ne $hold->usr) {
297                 if($apputils->check_user_perms($user->id, $user->home_ou, "VIEW_HOLDS")) {
298                         return OpenILS::Perm->new("VIEW_HOLDS");
299                 }
300         }
301         
302         return 1 unless (defined($hold->current_copy));
303
304         #return 2 unless (defined($hold->capture_time));
305
306         my $copy = $session->request(
307                 "open-ils.storage.direct.asset.copy.retrieve", $hold->current_copy )->gather(1);
308         return 1 unless $copy; # should be an exception
309
310         use Data::Dumper;
311         warn "Hold Copy in status check: " . Dumper($copy) . "\n\n";
312
313         return 4 if ($hold->capture_time and $copy->circ_lib eq $hold->pickup_lib);
314
315         my $transit = _fetch_hold_transit($session, $hold->id);
316         return 4 if(ref($transit) and defined($transit->dest_recv_time) ); 
317
318         return 3 if defined($hold->capture_time);
319
320         return 2;
321 }
322
323
324 sub _fetch_hold_transit {
325         my $session = shift;
326         my $holdid = shift;
327         return $session->request(
328                 "open-ils.storage.direct.action.hold_transit_copy.search.hold",
329                 $holdid )->gather(1);
330 }
331
332 __PACKAGE__->register_method(
333         method  => "capture_copy",
334         api_name        => "open-ils.circ.hold.capture_copy.barcode",
335         notes           => <<"  NOTE");
336         Captures a copy to fulfil a hold
337         Params is login session and copy barcode
338         login mus have COPY_CHECKIN permissions (since this is essentially
339         copy checkin)
340         NOTE
341
342 sub capture_copy {
343         my( $self, $client, $login_session, $barcode ) = @_;
344
345         my $user = $apputils->check_user_session($login_session);
346
347         if($apputils->check_user_perms($user->id, $user->home_ou, "COPY_CHECKIN")) {
348                 return OpenILS::Perm->new("COPY_CHECKIN");
349         }
350
351         my $session = $apputils->start_db_session();
352         my $copy = $session->request(
353                 "open-ils.storage.direct.asset.copy.search.barcode",
354                 $barcode )->gather(1);
355
356         warn "Capturing copy " . $copy->id . "\n";
357
358         # retrieve the hold copy maps for this copy
359         my $maps = $session->request(
360                 "open-ils.storage.direct.action.hold_copy_map.search.target_copy.atomic",
361                 $copy->id)->gather(1);
362
363         my @holdids = map { $_->hold } @$maps;
364
365         use Data::Dumper;
366         warn "Found possible holds\n" . Dumper(\@holdids) . "\n";
367
368         # retrieve sorted list of holds for the given maps and use the first
369         # if there is a hold for this lib, use that
370         my $holds = $session->request(
371                 "open-ils.storage.direct.action.hold_request.search_where.atomic",
372                 { id => \@holdids, current_copy => undef }, 
373                 { order_by => "CASE WHEN ". $copy->circ_lib .
374                         " = (SELECT a.home_ou FROM actor.usr a where a.id = usr ) THEN 0 ELSE 1 END, request_time" }
375                 )->gather(1);
376
377         my $hold = $holds->[0];
378
379         warn "Found hold " . $hold->id . "\n";
380
381         $hold->current_copy($copy->id);
382         $hold->capture_time("now"); # ???
383
384         #update the hold
385         my $stat = $session->request(
386                         "open-ils.storage.direct.action.hold_request.update", $hold)->gather(1);
387         if(!$stat) { throw OpenSRF::EX ("Error updating hold request " . $copy->id); }
388
389         $copy->status(8); #status on holds shelf
390
391         # if the staff member capturing this item is not at the pickup lib
392         if( $user->home_ou ne $hold->pickup_lib ) {
393                 my $trans = Fieldmapper::action::hold_transit_copy->new;
394                 $trans->hold($hold->id);
395                 $trans->source($user->home_ou);
396                 $trans->dest($hold->pickup_lib);
397                 $trans->source_send_time("now");
398                 my $meth = $self->method_lookup("open-ils.circ.hold_transit.create");
399                 my ($stat) = $meth->run( $login_session, $trans, $session );
400                 if(!$stat) { throw OpenSRF::EX ("Error creating new hold transit"); }
401                 else { $copy->status(6); } #status in transit 
402         }
403
404         $copy->editor($user->id);
405         $copy->edit_date("now");
406         $stat = $session->request(
407                 "open-ils.storage.direct.asset.copy.update", $copy )->gather(1);
408         if(!$stat) { throw OpenSRF::EX ("Error updating copy " . $copy->id); }
409
410         $apputils->commit_db_session($session);
411
412         return { route_to => $hold->pickup_lib };
413
414 }
415
416
417 __PACKAGE__->register_method(
418         method  => "create_hold_transit",
419         api_name        => "open-ils.circ.hold_transit.create",
420         notes           => <<"  NOTE");
421         Creates a new transit object
422         NOTE
423
424 sub create_hold_transit {
425         my( $self, $client, $login_session, $transit, $session ) = @_;
426
427         my $user = $apputils->check_user_session($login_session);
428         if($apputils->check_user_perms($user->id, $user->home_ou, "CREATE_TRANSIT")) {
429                 return OpenILS::Perm->new("CREATE_TRANSIT");
430         }
431         
432         my $ses;
433         if($session) { $ses = $session; } 
434         else { $ses = OpenSRF::AppSession->create("open-ils.storage"); }
435
436         return $ses->request(
437                 "open-ils.storage.direct.action.hold_transit_copy.create", $transit )->gather(1);
438 }
439
440
441
442
443 1;