LP#1438252 SIP honor floating copy checkin location
[working/Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / SIP / Transaction / Checkin.pm
1 package OpenILS::SIP::Transaction::Checkin;
2 use warnings; use strict;
3
4 use POSIX qw(strftime);
5 use Sys::Syslog qw(syslog);
6 use Data::Dumper;
7 use Time::HiRes q/time/;
8
9 use OpenILS::SIP;
10 use OpenILS::SIP::Transaction;
11 use OpenILS::Const qw/:const/;
12 use OpenILS::Application::AppUtils;
13 my $U = 'OpenILS::Application::AppUtils';
14
15 use base qw(OpenILS::SIP::Transaction);
16
17 my $debug = 0;
18
19 my %fields = (
20     magnetic => 0,
21     sort_bin => undef,
22     # 3M extensions: (most of the data is stored under Item)
23 #   collection_code  => undef,
24 #   call_number      => undef,
25     alert_type       => undef,  # 00,01,02,03,04 or 99
26 #   hold_patron_id   => undef,
27 #   hold_patron_name => "",
28 #   hold             => undef,
29 );
30
31 my $hold_as_transit = 0;
32
33 sub new {
34     my $class = shift;;
35     my $self = $class->SUPER::new(@_);              # start with an Transaction object
36
37     foreach (keys %fields) {
38         $self->{_permitted}->{$_} = $fields{$_};    # overlaying _permitted
39     }
40
41     @{$self}{keys %fields} = values %fields;        # copying defaults into object
42
43     $self->load_override_events;
44
45     $hold_as_transit = OpenILS::SIP->config->{implementation_config}->{checkin_hold_as_transit};
46
47     return bless $self, $class;
48 }
49
50 sub resensitize {
51     my $self = shift;
52     return 0 if !$self->{item};
53     return !$self->{item}->magnetic;
54 }
55
56 my %override_events;
57 sub load_override_events {
58     return if %override_events;
59     my $override = OpenILS::SIP->config->{implementation_config}->{checkin_override};
60     return unless $override;
61     my $events = $override->{event};
62     $events = [$events] unless ref $events eq 'ARRAY';
63     $override_events{$_} = 1 for @$events;
64 }
65
66 my %org_sn_cache;
67 sub do_checkin {
68     my $self = shift;
69     my ($sip_handler, $inst_id, $trans_date, $return_date, $current_loc, $item_props) = @_; # most unused
70
71     unless($self->{item}) {
72         $self->ok(0);
73         return undef;
74     }
75
76     $inst_id ||= '';
77
78     # physical location defaults to ws ou of the logged in sip user,
79     # which currently defaults to home_ou, since ws's aren't used.
80     my $phys_location = $sip_handler->{login_session}->ws_ou;
81
82     my $args = {barcode => $self->{item}->id};
83     $args->{hold_as_transit} = 1 if $hold_as_transit;
84
85     if($return_date) {
86         # SIP date format is YYYYMMDD.  Translate to ISO8601
87         $return_date =~ s/(\d{4})(\d{2})(\d{2}).*/$1-$2-$3/;
88         syslog('LOG_INFO', "Checking in with backdate $return_date");
89         $args->{backdate} = $return_date;
90     }
91
92     if($current_loc) { # SIP client specified a physical location
93
94         my $org_id = (defined $org_sn_cache{$current_loc}) ? 
95             $org_sn_cache{$current_loc} :
96             OpenILS::SIP->editor()->search_actor_org_unit({shortname => $current_loc}, {idlist => 1})->[0];
97
98         $org_sn_cache{$current_loc} = $org_id;
99
100         # if the caller specifies a physical location, use it as the checkin circ lib
101         $args->{circ_lib} = $phys_location = $org_id if defined $org_id;
102     }
103
104     my $override = 0;
105     my ($resp, $txt, $code);
106
107     while(1) {
108
109         my $method = 'open-ils.circ.checkin';
110         $method .= '.override' if $override;
111
112         my $start_time = time();
113         $resp = $U->simplereq('open-ils.circ', $method, $self->{authtoken}, $args);
114         syslog('LOG_INFO', "OILS: Checkin API call took %0.3f seconds", (time() - $start_time));
115
116         if ($debug) {
117             my $s = Dumper($resp);
118             $s =~ s/\n//mog;
119             syslog('LOG_INFO', "OILS: Checkin response: $s");
120         }
121
122         # In oddball cases, we can receive an array of events.
123         # The first event received should be treated as the main result.
124         $resp = $$resp[0] if ref($resp) eq 'ARRAY';
125         $code = $U->event_code($resp);
126         $txt  = (defined $code) ? $resp->{textcode} : '';
127
128         last if $override;
129
130         if ( $override_events{$txt} ) {
131             $override = 1;
132         } else {
133             last;
134         }
135     }
136
137     syslog('LOG_INFO', "OILS: Checkin resulted in event: $txt, phys_location: $phys_location");
138
139     $resp->{org} &&= OpenILS::SIP::shortname_from_id($resp->{org}); # Convert id to shortname
140
141     $self->item->destination_loc($resp->{org}) if $resp->{org};
142
143     if ($txt eq 'ROUTE_ITEM') {
144         # Note, this alert_type will be overridden below if this is a hold transit
145         $self->alert_type('04'); # send to other branch
146
147     } elsif ($txt and $txt ne 'NO_CHANGE' and $txt ne 'SUCCESS') {
148         syslog('LOG_WARNING', "OILS: Checkin returned unexpected event $code : $txt");
149         $self->alert_type('00'); # unknown
150     }
151     
152     my $payload = $resp->{payload} || {};
153
154     my ($circ, $copy);
155
156     if(ref $payload eq 'HASH') {
157
158         # Two places to look for hold data.  These are more important and more definitive than above.
159         if ($payload->{remote_hold}) {
160             # actually only used for checkin at non-owning branch w/ hold at same branch
161             $self->item->hold($payload->{remote_hold});     
162
163         } elsif ($payload->{hold}) {
164             $self->item->hold($payload->{hold});
165         }
166
167         $circ = $resp->{payload}->{circ} || '';
168         $copy = $resp->{payload}->{copy} || '';
169     }
170
171     if ($copy) {
172         # Checkin of floating copies changes the circ lib.
173         # Update our SIP "item" to reflect the change.
174
175         if ($copy->circ_lib != $self->item->{copy}->circ_lib->id) {
176             syslog('LOG_INFO', "OILS: updating copy circ lib after checkin");
177
178             $self->item->{copy}->circ_lib(
179                 OpenILS::SIP->editor()
180                     ->retrieve_actor_org_unit($copy->circ_lib)
181             );
182         }
183     }
184
185     if ($self->item->hold) {
186         my ($pickup_lib_id, $pickup_lib_sn);
187
188         my $holder = OpenILS::SIP->editor()->retrieve_actor_user(
189             [$self->item->hold->usr, {flesh => 1, flesh_fields => {au => ['card']}}]);
190
191         my $holder_name = OpenILS::SIP::Patron::format_name($holder);
192
193         if (ref $self->item->hold->pickup_lib) {
194             $pickup_lib_id = $self->item->hold->pickup_lib->id;
195             $pickup_lib_sn = $self->item->hold->pickup_lib->shortname;
196
197         } else {
198             $pickup_lib_id = $self->item->hold->pickup_lib;
199             $pickup_lib_sn = OpenILS::SIP::shortname_from_id($pickup_lib_id);
200         }
201
202         $self->item->hold_patron_bcode( ($holder->card) ? $holder->card->barcode : '');
203         $self->item->hold_patron_name($holder_name);
204         $self->item->destination_loc($pickup_lib_sn); 
205
206         my $atype = ($pickup_lib_id == $phys_location) ? '01' : '02';
207         $self->alert_type($atype);
208     }
209
210     $self->alert(1) if defined $self->alert_type;  # alert_type could be "00", hypothetically
211
212     if ( $circ ) {
213         $self->{circ_user_id} = $circ->usr;
214         $self->ok(1);
215     } elsif ($txt eq 'NO_CHANGE' or $txt eq 'SUCCESS' or $txt eq 'ROUTE_ITEM') {
216         $self->ok(1); # NO_CHANGE means it wasn't checked out anyway, no problem
217     } else {
218         $self->alert(1);
219         $self->alert_type('00') unless $self->alert_type; # wasn't checked out, but *something* changed
220         # $self->ok(0); # maybe still ok?
221     }
222 }
223
224 1;
225 __END__
226
227 Successful Checkin event payload includes:
228     $payload->{copy}   (unfleshed)
229     $payload->{record} 
230     $payload->{circ}   
231     $payload->{transit}
232     $payload->{cancelled_hold_transit}
233     $payload->{hold}   
234     $payload->{patron} 
235
236 Some EVENT strings:
237     SUCCESS                => ,
238     ASSET_COPY_NOT_FOUND   => ,
239     NO_CHANGE              => ,
240     PERM_FAILURE           => ,
241     CIRC_CLAIMS_RETURNED   => ,
242     COPY_ALERT_MESSAGE     => ,
243     COPY_STATUS_LOST       => ,
244     COPY_STATUS_MISSING    => ,
245     COPY_BAD_STATUS        => ,
246     ITEM_DEPOSIT_PAID      => ,
247     ROUTE_ITEM             => ,
248     DATABASE_UPDATE_FAILED => ,
249     DATABASE_QUERY_FAILED  => ,
250
251 # alert_type:
252 #   00 - Unknown
253 #   01 - hold in local library
254 #   02 - hold for other branch
255 #   03 - hold for ILL (not used in EG)
256 #   04 - send to other branch (no hold)
257 #   99 - Other