1 package OpenILS::SIP::Item;
2 use strict; use warnings;
4 use Sys::Syslog qw(syslog);
8 use OpenILS::SIP::Transaction;
9 use OpenILS::Application::AppUtils;
10 use OpenILS::Application::Circ::ScriptBuilder;
12 use OpenILS::Const qw/:const/;
13 use OpenSRF::Utils qw/:datetime/;
14 use DateTime::Format::ISO8601;
15 use OpenSRF::Utils::SettingsClient;
16 my $U = 'OpenILS::Application::AppUtils';
21 # 1 means read/write Actually, gloves are off. Set what you like.
25 # sip_media_type => 0,
26 sip_item_properties => 0,
27 # magnetic_media => 0,
28 permanent_location => 0,
29 current_location => 0,
35 hold_patron_bcode => 0,
36 hold_patron_name => 0,
53 sub DESTROY { } # keeps AUTOLOAD from catching inherent DESTROY calls
57 my $class = ref($self) or croak "$self is not an object";
62 unless (exists $fields{$name}) {
63 croak "Cannot access '$name' field of class '$class'";
67 # $fields{$name} or croak "Field '$name' of class '$class' is READ ONLY."; # nah, go ahead
68 return $self->{$name} = shift;
70 return $self->{$name};
76 my ($class, $item_id) = @_;
77 my $type = ref($class) || $class;
78 my $self = bless( {}, $type );
80 syslog('LOG_DEBUG', "OILS: Loading item $item_id...");
81 return undef unless $item_id;
83 my $e = OpenILS::SIP->editor();
85 my $copy = $e->search_asset_copy(
87 { barcode => $item_id, deleted => 'f' },
91 acp => [ 'circ_lib', 'call_number', 'status' ],
92 acn => [ 'owning_lib', 'record' ],
102 syslog("LOG_DEBUG", "OILS: Item '%s' : not found", $item_id);
106 my ($circ) = $U->fetch_open_circulation($copy->id);
108 # if i am checked out, set $self->{patron} to the user's barcode
109 my $user = $e->retrieve_actor_user(
112 { flesh => 1, flesh_fields => { "au" => [ 'card' ] } }
116 my $bc = ($user) ? $user->card->barcode : "";
117 $self->{patron} = $bc;
118 $self->{patron_object} = $user;
120 syslog('LOG_DEBUG', "OILS: Open circulation exists on $item_id : user = $bc");
123 $self->{id} = $item_id;
124 $self->{copy} = $copy;
125 $self->{volume} = $copy->call_number;
126 $self->{record} = $copy->call_number->record;
127 $self->{call_number} = $copy->call_number->label;
128 $self->{mods} = $U->record_to_mvr($self->{record}) if $self->{record}->marc;
130 if ($copy->status->id == OILS_COPY_STATUS_IN_TRANSIT) {
131 my $transit = $e->search_action_transit_copy([
133 target_copy => $copy->id, # NOT barcode ($self->id)
134 dest_recv_time => undef
143 # warn "Item transit: " . Dumper($transit) . "\nItem transit->dest: " . Dumper($transit->dest);;
145 $self->{destination_loc} = $transit->[0]->dest->shortname;
147 syslog('LOG_WARNING', "OILS: Item('$item_id') status is In Transit, but no action.transit_copy found!");
151 syslog("LOG_DEBUG", "OILS: Item('$item_id'): found with title '%s'", $self->title_id);
153 my $config = OpenILS::SIP->config(); # FIXME : will not always match!
155 my $legacy = $config->{implementation_config}->{legacy_script_support} || undef;
156 if( defined $legacy ) {
157 $self->{legacy_script_support} = ($legacy =~ /t(rue)?/io) ? 1 : 0;
158 syslog("LOG_DEBUG", "legacy_script_support is set in SIP config: " . $self->{legacy_script_support});
160 my $lss = OpenSRF::Utils::SettingsClient->new->config_value(
161 apps => 'open-ils.circ',
162 app_settings => 'legacy_script_support'
164 $self->{legacy_script_support} = ($lss =~ /t(rue)?/io) ? 1 : 0;
165 syslog("LOG_DEBUG", "legacy_script_support is set in SRF config: " . $self->{legacy_script_support});
171 sub run_attr_script {
173 return 1 if $self->{ran_script};
174 $self->{ran_script} = 1;
176 if($self->{legacy_script_support}){
178 syslog('LOG_DEBUG', "Legacy script support is ON");
179 my $config = OpenILS::SIP->config();
180 my $path = $config->{implementation_config}->{scripts}->{path};
181 my $item_config_script = $config->{implementation_config}->{scripts}->{item_config};
183 $path = ref($path) eq 'ARRAY' ? $path : [$path];
184 my $path_str = join(", ", @$path);
186 syslog('LOG_DEBUG', "OILS: Script path = [$path_str], Item config script = $item_config_script");
188 my $runner = OpenILS::Application::Circ::ScriptBuilder->build({
189 copy => $self->{copy},
190 editor => OpenILS::SIP->editor(),
193 $runner->add_path($_) for @$path;
194 $runner->load($item_config_script);
196 unless( $self->{item_config_result} = $runner->run ) { # assignment, not comparison
198 warn "Item config script [$path_str : $item_config_script] failed to run: $@\n";
199 syslog('LOG_ERR', "OILS: Item config script [$path_str : $item_config_script] failed to run: $@");
207 # use the in-db circ modifier configuration
208 my $config = {magneticMedia => 'f', SIPMediaType => '001'}; # defaults
209 my $mod = $self->{copy}->circ_modifier;
212 my $mod_obj = OpenILS::SIP->editor()->search_config_circ_modifier($mod);
214 $config->{magneticMedia} = $mod_obj->magnetic_media;
215 $config->{SIPMediaType} = $mod_obj->sip2_media_type;
219 $self->{item_config_result} = { item_config => $config };
231 return 0 unless $self->run_attr_script;
232 my $mag = $self->{item_config_result}->{item_config}->{magneticMedia} || '';
233 syslog('LOG_DEBUG', "OILS: magnetic = $mag");
234 return ($mag and $mag =~ /t(rue)?/io) ? 1 : 0;
239 return 0 unless $self->run_attr_script;
240 my $media = $self->{item_config_result}->{item_config}->{SIPMediaType} || '';
241 syslog('LOG_DEBUG', "OILS: media type = $media");
242 return ($media) ? $media : '001';
247 my $t = ($self->{mods}) ? $self->{mods}->title : $self->{copy}->dummy_title;
248 return OpenILS::SIP::clean_text($t);
251 sub permanent_location {
253 return OpenILS::SIP::clean_text($self->{copy}->circ_lib->shortname);
256 sub current_location {
258 return OpenILS::SIP::clean_text($self->{copy}->circ_lib->shortname);
267 # 05 Charged; not to be recalled until earliest recall date
270 # 08 Waiting on hold shelf
271 # 09 Waiting to be re-shelved
272 # 10 In transit between library locations
273 # 11 Claimed returned
276 sub sip_circulation_status {
278 my $stat = $self->{copy}->status->id;
280 return '02' if $stat == OILS_COPY_STATUS_ON_ORDER;
281 return '03' if $stat == OILS_COPY_STATUS_AVAILABLE;
282 return '04' if $stat == OILS_COPY_STATUS_CHECKED_OUT;
283 return '06' if $stat == OILS_COPY_STATUS_IN_PROCESS;
284 return '08' if $stat == OILS_COPY_STATUS_ON_HOLDS_SHELF;
285 return '09' if $stat == OILS_COPY_STATUS_RESHELVING;
286 return '10' if $stat == OILS_COPY_STATUS_IN_TRANSIT;
287 return '12' if $stat == OILS_COPY_STATUS_LOST;
288 return '13' if $stat == OILS_COPY_STATUS_MISSING;
293 sub sip_security_marker {
294 return '02'; # FIXME? 00-other; 01-None; 02-Tattle-Tape Security Strip (3M); 03-Whisper Tape (3M)
298 return '01'; # FIXME? 01-09 enumerated in spec. We just use O1-other/unknown.
309 return OpenILS::SIP->config()->{implementation_config}->{currency};
314 return OpenILS::SIP::clean_text($self->{copy}->circ_lib->shortname);
322 sub hold_queue_position { # TODO
323 my ($self, $patron_id) = @_;
330 # this should force correct circ fetching
331 require OpenILS::Utils::CStoreEditor;
332 my $e = OpenILS::Utils::CStoreEditor->new(xact => 1);
333 #my $e = OpenILS::SIP->editor();
335 my $circ = $e->search_action_circulation(
336 { target_copy => $self->{copy}->id, checkin_time => undef } )->[0];
341 syslog('LOG_INFO', "OILS: No open circ found for copy");
345 my $due = OpenILS::SIP->format_date($circ->due_date, 'due');
346 syslog('LOG_DEBUG', "OILS: Found item due date = $due");
350 sub recall_date { # TODO
355 sub hold_pickup_date { # TODO
360 # message to display on console
363 return OpenILS::SIP::clean_text($self->{screen_msg}) || '';
370 return OpenILS::SIP::clean_text($self->{print_line}) || '';
374 # An item is available for a patron if
375 # 1) It's not checked out and (there's no hold queue OR patron
376 # is at the front of the queue)
378 # 2) It's checked out to the patron and there's no hold queue
380 my ($self, $for_patron) = @_;
382 my $stat = $self->{copy}->status->id;
384 $stat == OILS_COPY_STATUS_AVAILABLE or
385 $stat == OILS_COPY_STATUS_RESHELVING;
396 OpenILS::SIP::Item - SIP abstraction layer for OpenILS Items.
400 =head2 owning_lib vs. circ_lib
402 In Evergreen, owning_lib is the org unit that purchased the item, the place to which the item
403 should return after it's done rotating/floating to other branches (via staff intervention),
404 or some combination of those. The owning_lib, however, is not necessarily where the item
405 should be going "right now" or where it should return to by default. That would be the copy
406 circ_lib or the transit destination. (In fact, the item may B<never> go to the owning_lib for
407 its entire existence). In the context of SIP, the circ_lib more accurately describes the item's
408 permanent location, i.e. where it needs to be sent if it's not en route to somewhere else.
410 This confusion extends also to the SIP extension field of "owner". It means that the SIP owner does not
411 correspond to EG's asset.volume.owning_lib, mainly because owning_lib is effectively the "ultimate
412 owner" but not necessarily the "current owner". Because we populate SIP fields with circ_lib, the
413 owning_lib is unused by SIP.