Fix in-transit hold retarget
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Application / Circ / Transit.pm
1 package OpenILS::Application::Circ::Transit;
2 use base 'OpenILS::Application';
3 use strict; use warnings;
4 use OpenSRF::EX qw(:try);
5 use Data::Dumper;
6 use OpenSRF::Utils;
7 use OpenSRF::Utils::Cache;
8 use Digest::MD5 qw(md5_hex);
9 use OpenILS::Utils::CStoreEditor qw/:funcs/;
10 use OpenILS::Application::AppUtils;
11 use OpenILS::Application::Circ::Holds;
12 use OpenSRF::Utils::Logger qw(:logger);
13 use OpenSRF::AppSession;
14 use OpenILS::Const qw/:const/;
15
16 my $U                                                   = "OpenILS::Application::AppUtils";
17 my $holdcode                            = "OpenILS::Application::Circ::Holds";
18 $Data::Dumper::Indent   = 0;
19
20
21
22 __PACKAGE__->register_method(
23         method  => "copy_transit_receive",
24         api_name        => "open-ils.circ.copy_transit.receive",
25         notes           => q/
26                 Closes out a copy transit
27                 Requestor needs the COPY_TRANSIT_RECEIVE permission
28                 @param authtoken The login session key
29                 @param params An object of named params including
30                         copyid - the id of the copy in quest
31                         barcode - the barcode of the copy in question 
32                                 If copyid is not sent, this is used.
33                 @return A ROUTE_ITEM if the copy is destined for a different location.
34                         A SUCCESS event on success. Other events on error.
35         /);
36
37 sub copy_transit_receive {
38         my( $self, $client, $authtoken, $params ) = @_;
39         my %params = %$params;
40         my( $evt, $copy, $requestor );
41         ($requestor, $evt) = $U->checksesperm($authtoken, 'COPY_TRANSIT_RECEIVE');
42         return $evt if $evt;
43         ($copy, $evt) = $U->fetch_copy($params{copyid});
44         ($copy, $evt) = $U->fetch_copy_by_barcode($params{barcode}) unless $copy;
45         return $evt if $evt;
46         my $session = $U->start_db_session();
47         $U->set_audit_info($session, $authtoken, $requestor->id, $requestor->wsid);
48         $evt = transit_receive( $self, $copy, $requestor, $session );
49         $U->commit_db_session($session) if $U->event_equals($evt,'SUCCESS');
50         return $evt;
51 }
52
53 # ------------------------------------------------------------------------------
54 # If the transit destination is different than the requestor's lib,
55 # a ROUTE_TO event is returned with the org set.
56 # If 
57 # ------------------------------------------------------------------------------
58 sub transit_receive {
59         my ( $class, $copy, $requestor, $session ) = @_;
60         $U->logmark;
61
62         my( $transit, $evt );
63         my $copyid = $copy->id;
64
65         my $status_name = $U->copy_status_to_name($copy->status);
66         $logger->debug("Attempting transit receive on copy $copyid. Copy status is $status_name");
67
68         # fetch the transit
69         ($transit, $evt) = $U->fetch_open_transit_by_copy($copyid);
70         return $evt if $evt;
71
72         if( $transit->dest != $requestor->home_ou ) {
73                 $logger->activity("Fowarding transit on copy which is destined ".
74                         "for a different location. copy=$copyid,current ".
75                         "location=".$requestor->home_ou.",destination location=".$transit->dest);
76
77                 return OpenILS::Event->new('ROUTE_ITEM', org => $transit->dest );
78         }
79
80         # The transit is received, set the receive time
81         $transit->dest_recv_time('now');
82         my $r = $session->request(
83                 'open-ils.storage.direct.action.transit_copy.update', $transit )->gather(1);
84         return $U->DB_UPDATE_FAILED($transit) unless $r;
85
86         my $ishold      = 0;
87         my ($ht)                = $U->fetch_hold_transit( $transit->id );
88         if($ht) {
89                 $logger->info("Hold transit found in transit receive...");
90                 $ishold = 1;
91         }
92
93         $logger->info("Recovering original copy status in transit: ".$transit->copy_status);
94         $copy->status( $transit->copy_status );
95         return $evt if ( $evt = 
96                 $U->update_copy( copy => $copy, editor => $requestor->id, session => $session ));
97
98         return OpenILS::Event->new('SUCCESS', ishold => $ishold, 
99                 payload => { transit => $transit, holdtransit => $ht } );
100 }
101
102
103
104
105
106 __PACKAGE__->register_method(
107         method  => "copy_transit_create",
108         api_name        => "open-ils.circ.copy_transit.create",
109         notes           => q/
110                 Creates a new copy transit.  Requestor must have the 
111                 CREATE_COPY_TRANSIT permission.
112                 @param authtoken The login session key
113                 @param params A param object containing the following keys:
114                         copyid          - the copy id
115                         destination     - the id of the org destination.  If not defined,
116                                 defaults to the copy's circ_lib
117                 @return SUCCESS event on success, other event on error
118         /);
119
120 sub copy_transit_create {
121
122         my( $self, $client, $authtoken, $params ) = @_;
123         my %params = %$params;
124
125         my( $requestor, $evt ) = 
126                 $U->checksesperm( $authtoken, 'CREATE_COPY_TRANSIT' );
127         return $evt if $evt;
128
129         my $copy;
130         ($copy,$evt) = $U->fetch_copy($params{copyid});
131         return $evt if $evt;
132
133         my $session             = $params{session} || $U->start_db_session();
134         my $source              = $requestor->home_ou;
135         my $dest                        = $params{destination} || $copy->circ_lib;
136         my $transit             = Fieldmapper::action::transit_copy->new;
137         $U->set_audit_info($session, $authtoken, $requestor->id, $requestor->wsid);
138
139         $logger->activity("User ". $requestor->id ." creating a ".
140                 " new copy transit for copy ".$copy->id." to org $dest");
141
142         $transit->source($source);
143         $transit->dest($dest);
144         $transit->target_copy($copy->id);
145         $transit->source_send_time("now");
146         $transit->copy_status($copy->status);
147         
148         $logger->debug("Creating new copy_transit in DB");
149
150         my $s = $session->request(
151                 "open-ils.storage.direct.action.transit_copy.create", $transit )->gather(1);
152         return $U->DB_UPDATE_FAILED($transit) unless $s;
153         
154         my $stat = $U->copy_status_from_name('in transit');
155
156         $copy->status($stat->id); 
157         return $evt if ($evt = $U->update_copy(
158                 copy => $copy, editor => $requestor->id, session => $session ));
159
160         $U->commit_db_session($session) unless $params{session};
161
162         return OpenILS::Event->new('SUCCESS', 
163                 payload => { copy => $copy, transit => $transit } );
164 }
165
166
167 __PACKAGE__->register_method(
168         method => 'abort_transit',
169         api_name        => 'open-ils.circ.transit.abort',
170         signature       => q/
171                 Deletes a cleans up a transit
172         /
173 );
174
175 sub abort_transit {
176         my( $self, $conn, $authtoken, $params ) = @_;
177
178         my $copyid              = $$params{copyid};
179         my $barcode             = $$params{barcode};
180         my $transitid   = $$params{transitid};
181
182         my $copy;
183         my $transit;
184         my $evt;
185
186         my $e = new_editor(xact => 1, authtoken => $authtoken);
187         return $e->event unless $e->checkauth;
188         return $e->event unless $e->allowed('ABORT_TRANSIT');
189
190         # ---------------------------------------------------------------------
191         # Find the related copy and/or transit based on whatever data we have
192         if( $barcode ) {
193                 $copy = $e->search_asset_copy({barcode=>$barcode, deleted => 'f'})->[0];
194                 return $e->event unless $copy;
195
196         } elsif( $copyid ) {
197                 $copy = $e->retrieve_asset_copy($copyid) or return $e->event;
198         }
199
200         if( $transitid ) {
201                 $transit = $e->retrieve_action_transit_copy($transitid)
202                         or return $e->event;
203
204         } elsif( $copy ) {
205
206                 $transit = $e->search_action_transit_copy(
207                         { target_copy => $copy->id, dest_recv_time => undef })->[0];
208                 return $e->event unless $transit;
209         }
210
211         if($transit and !$copy) {
212                 $copy = $e->retrieve_asset_copy($transit->target_copy)
213                         or return $e->event;
214         }
215         # ---------------------------------------------------------------------
216
217         return __abort_transit( $e, $transit, $copy );
218 }
219
220
221
222 sub __abort_transit {
223
224         my( $e, $transit, $copy, $no_reset_hold, $no_commit ) = @_;
225
226         my $evt;
227         my $hold;
228
229         if( ($transit->copy_status == OILS_COPY_STATUS_LOST and !$e->allowed('ABORT_TRANSIT_ON_LOST')) or
230                 ($transit->copy_status == OILS_COPY_STATUS_MISSING and !$e->allowed('ABORT_TRANSIT_ON_MISSING')) ) {
231                 $e->rollback;
232                 return OpenILS::Event->new('TRANSIT_ABORT_NOT_ALLOWED', copy_status => $transit->copy_status);
233         }
234
235
236         if( $transit->dest != $e->requestor->ws_ou 
237                 and $transit->source != $e->requestor->ws_ou ) {
238                 return $e->die_event unless $e->allowed('ABORT_REMOTE_TRANSIT', $e->requestor->ws_ou);
239         }
240
241         # recover the copy status
242         $copy->status( $transit->copy_status );
243         $copy->editor( $e->requestor->id );
244         $copy->edit_date('now');
245
246         my $holdtransit = $e->retrieve_action_hold_transit_copy($transit->id);
247
248         if( $holdtransit ) {
249                 $logger->info("setting copy to reshelving on hold transit abort");
250                 $copy->status( OILS_COPY_STATUS_RESHELVING );
251         }
252
253         return $e->die_event unless $e->delete_action_transit_copy($transit);
254         return $e->die_event unless $e->update_asset_copy($copy);
255
256         $e->commit unless $no_commit;
257
258         # if this is a hold transit, un-capture/un-target the hold
259         if($holdtransit and !$no_reset_hold) {
260                 $hold = $e->retrieve_action_hold_request($holdtransit->hold) 
261             or return $e->die_event;
262                 $evt = $holdcode->_reset_hold( $e->requestor, $hold );
263                 return $evt if $evt;
264         }
265
266         return 1;
267 }
268
269
270 __PACKAGE__->register_method(
271         method          => 'get_open_copy_transit',
272         api_name                => 'open-ils.circ.open_copy_transit.retrieve',
273         signature       => q/
274                 Retrieves the open transit object for a given copy
275                 @param auth The login session key
276                 @param copyid The id of the copy
277                 @return Transit object
278  /
279 );
280
281 sub get_open_copy_transit {
282         my( $self, $conn, $auth, $copyid ) = @_;        
283         my $e = new_editor(authtoken=>$auth);
284         return $e->event unless $e->checkauth;
285         return $e->event unless $e->allowed('VIEW_USER'); # XXX rely on editor perms
286         my $t = $e->search_action_transit_copy(
287                 { target_copy => $copyid, dest_recv_time => undef });
288         return $e->event unless @$t;
289         return $$t[0];
290 }
291
292
293
294 __PACKAGE__->register_method(
295         method => 'fetch_transit_by_copy',
296         api_name => 'open-ils.circ.fetch_transit_by_copy',
297 );
298
299 sub fetch_transit_by_copy {
300         my( $self, $conn, $auth, $copyid ) = @_;
301         my $e = new_editor(authtoken=>$auth);
302         return $e->event unless $e->checkauth;
303         my $t = $e->search_action_transit_copy(
304                 {
305                         target_copy => $copyid,
306                         dest_recv_time => undef
307                 }
308         )->[0];
309         return $e->event unless $t;
310         my $ht = $e->retrieve_action_hold_transit_copy($t->id);
311         return { atc => $t, ahtc => $ht };
312 }
313
314
315
316 __PACKAGE__->register_method(
317         method => 'transits_by_lib',
318         api_name => 'open-ils.circ.transit.retrieve_by_lib',
319 );
320
321
322 # start_date and end_date are optional endpoints for the transit creation date
323 sub transits_by_lib {
324         my( $self, $conn, $auth, $orgid, $start_date, $end_date ) = @_;
325         my $e = new_editor(authtoken=>$auth);
326         return $e->event unless $e->checkauth;
327         return $e->event unless $e->allowed('VIEW_CIRCULATIONS'); # eh.. basically the same permission
328
329     my $order_by = {order_by => { atc => 'source_send_time' }};
330     my $search = { dest_recv_time => undef };
331
332     if($end_date) {
333         if($start_date) {
334             $search->{source_send_time} = {between => [$start_date, $end_date]};
335         } else {
336             $search->{source_send_time} = {'<=' => $end_date};
337         }
338     } elsif($start_date) {
339         $search->{source_send_time} = {'>=' => $start_date};
340     }
341
342     $search->{dest} = $orgid;
343
344         my $tos = $e->search_action_transit_copy([ $search, $order_by ], {idlist=>1});
345
346     delete $$search{dest};
347     $search->{source} = $orgid;
348
349         my $froms = $e->search_action_transit_copy([ $search, $order_by ], {idlist=>1});
350
351         return { from => $froms, to => $tos };
352 }
353
354
355
356 __PACKAGE__->register_method(
357         method => 'fetch_transit',
358         api_name => 'open-ils.circ.transit.retrieve',
359 );
360 sub fetch_transit {
361         my( $self, $conn, $auth, $transid ) = @_;
362         my $e = new_editor(authtoken=>$auth);
363         return $e->event unless $e->checkauth;
364         return $e->event unless $e->allowed('VIEW_CIRCULATIONS'); # eh.. basically the same permission
365
366         my $ht = $e->retrieve_action_hold_transit_copy($transid);
367         return $ht if $ht;
368
369         my $t = $e->retrieve_action_transit_copy($transid)
370                 or return $e->event;
371         return $t;
372 }
373
374         
375
376
377
378
379 1;