use OpenILS::Application::SIP2::Item;
use OpenILS::Application::SIP2::Patron;
use OpenILS::Application::SIP2::Checkout;
+use OpenILS::Application::SIP2::Checkin;
my $U = 'OpenILS::Application::AppUtils';
my $SC = 'OpenILS::Application::SIP2::Common';
}
my $MESSAGE_MAP = {
+ '09' => \&handle_checkin,
'11' => \&handle_checkout,
'17' => \&handle_item_info,
'23' => \&handle_patron_status,
{AB => $barcode},
{AH => $details->{due_date}},
{AJ => $details->{title}},
- {AP => $details->{item}->circ_lib->shortname},
- {AQ => $details->{item}->circ_lib->shortname},
- {BG => $details->{item}->circ_lib->shortname},
+ {AP => $details->{current_loc}},
+ {AQ => $details->{permanent_loc}},
+ {BG => $details->{owning_loc}},
{BH => $config->{settings}->{currency}},
{BV => $details->{item}->deposit_amount},
{CF => $details->{hold_queue_length}},
{CK => $details->{media_type}},
{CM => $details->{hold_pickup_date}},
+ {CT => $details->{destination_loc}},
{CY => $details->{hold_patron_barcode}}
]
};
$session, barcode => $patron_barcode
);
- if (!$item_details || !$patron_details) { # bad data
- return {
- code => '12',
- fixed_fields => [
- 0, # checkout ok
- $SC->sipbool(0), # renewal ok
- $SC->sipbool(0), # magnetic media
- $SC->sipbool(0), # desensitize
- $SC->sipdate, # transaction date
- ],
- fields => [
- {AA => $patron_barcode},
- {AB => $item_barcode},
- ]
- };
- }
+ return {
+ code => '12',
+ fixed_fields => [
+ 0, # checkout ok
+ $SC->sipbool(0), # renewal ok
+ $SC->sipbool(0), # magnetic media
+ $SC->sipbool(0), # desensitize
+ $SC->sipdate, # transaction date
+ ],
+ fields => [
+ {AA => $patron_barcode},
+ {AB => $item_barcode},
+ ]
+ } unless ($item_details && $patron_details);
my $circ_details = OpenILS::Application::SIP2::Checkout->checkout(
$session,
fee_ack => $fee_ack
);
- use Data::Dumper;
- $Data::Dumper::Indent = 0;
- $logger->info("CHECKOUT RESULTED IN ".Dumper($circ_details));
my $magnetic = $item_details->{magnetic_media};
- my $can_renew = 0;
-
+ my $deposit = $item_details->{item}->deposit_amount;
+ my $msg = $circ_details->{screen_msg};
my $circ = $circ_details->{circ};
+
+ my $can_renew = 0;
if ($circ) {
$can_renew = !$patron_details->{renew_denied}
&& $circ->renewal_remaining > 0;
}
- my $response = {
+ return {
code => '12',
fixed_fields => [
$circ ? 1 : 0, # checkout ok
fields => [
{AA => $patron_barcode},
{AB => $item_barcode},
+ $msg ? {AF => $msg} : (),
+ $circ ? {AH => $circ_details->{due_date}} : ();
{AJ => $item_details->{title}},
+ $circ ? {BK => $circ->id} : (),
{AO => $config->{institution}},
{BT => $item_details->{fee_type}},
+ $deposit ? {BV => $deposit} : (),
{CI => 0}, # security inhibit
{CK => $item_details->{media_type}}
]
};
+}
- if ($circ) {
- $SC->add_field($response, 'AH', $circ_details->{due_date});
- $SC->add_field($response, 'BK', $circ->id);
+sub handle_checkin {
+ my ($session, $message) = @_;
+ my $config = $session->config;
+
+ my $item_barcode = $SC->get_field_value($message, 'AB');
+ my $current_loc = $SC->get_field_value($message, 'AP');
+
+ my $item_details = OpenILS::Application::SIP2::Item->get_item_details(
+ $session, barcode => $item_barcode
+ );
+
+ return {
+ code => '10',
+ fixed_fields => [
+ 0, # checkin ok
+ $SC->sipbool(0), # resensitize
+ $SC->sipbool(0), # magnetic media
+ 'N' # alert
+ $SC->sipdate, # transaction date
+ ],
+ fields => [
+ {AA => $patron_barcode},
+ {AO => $config->{institution}},
+ ]
+ } unless $item_details;
- $SC->maybe_add_field(
- $response, 'BV', $item_details->{item}->deposit_amount);
+ my $checkin_details = OpenILS::Application::SIP2::Checkin->checkin(
+ $session,
+ item_barcode => $item_barcode,
+ current_loc => $current_loc,
+ item_details => $item_details
+ );
+
+ return {
+ code => '10',
+ fixed_fields => [
+ 0, # checkin ok
+ $SC->sipbool(0), # resensitize
+ $SC->sipbool(0), # magnetic media
+ 'Y' # alert
+ $SC->sipdate, # transaction date
+ ],
+ fields => [
+ {AA => $patron_barcode},
+ {AO => $config->{institution}},
+ {CV => '00'} # unkown alert type
+ ]
}
- $SC->maybe_add_field($response, 'AF', $circ_details->{screen_msg});
+ my $msg = $checkin_details->{screen_msg};
+ my $magnetic = $item_details->{magnetic_media};
- return $response;
+ return {
+ fixed_fields => [
+ $checkin_details->{ok}, # checkin ok
+ $SC->sipbool(!$magnetic), # resensitize
+ $SC->sipbool($magnetic), # magnetic media
+ $SC->sipbool($checkin_details->{alert}), # alert
+ $SC->sipdate, # transaction date
+ ],
+ fields => [
+ {AA => $patron_barcode},
+ {AB => $item_barcode},
+ $msg ? {AF => $msg} : (),
+ {AJ => $item_details->{title}},
+ {AO => $config->{institution}},
+ {AP => $checkin_details->{current_loc}},
+ {AQ => $checkin_details->{permanent_loc}},
+ {BG => $item_details->{owning_loc}},
+ {BT => $item_details->{fee_type}},
+ {CI => 0}, # security inhibit
+ {CK => $item_details->{media_type}},
+ {CV => $checkin_details->{alert_type}},
+ {CT => $details->{destination_loc}}
+ ]
+ };
}
+
1;
--- /dev/null
+package OpenILS::Application::SIP2::Checkin;
+use strict; use warnings;
+use DateTime;
+use DateTime::Format::ISO8601;
+use OpenSRF::System;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenSRF::Utils::Logger q/$logger/;
+use OpenILS::Application::AppUtils;
+use OpenILS::Utils::DateTime qw/:datetime/;
+use OpenILS::Const qw/:const/;
+use OpenILS::Application::SIP2::Common;
+use OpenILS::Application::SIP2::Session;
+my $U = 'OpenILS::Application::AppUtils';
+my $SC = 'OpenILS::Application::SIP2::Common';
+
+
+sub checkin {
+ my ($class, $session, %params) = @_;
+
+ my $details = {};
+ my $override = 0;
+
+ for (0, 1) { # 2 checkin requests max
+
+ $override = $class->perform_checkin(
+ $session, $details, $override, %params);
+
+ last unless $override;
+ }
+
+ return $details;
+}
+
+
+# Returns 1 if the checkin should be performed again with override.
+# Returns 0 if there's nothing left to do (final success / error)
+# Updates $details along the way.
+sub perform_checkin {
+ my ($class, $session, $details, $override, %params) = @_;
+ my $config = $session->config;
+ my $item_details = $params{item_details};
+
+ my $args = {
+ copy_barcode => $params{item_barcode},
+ holds_as_transit => $config->{checkin_hold_as_transit}
+ };
+
+ $args->{circ_lib} =
+ $SC->org_id_from_sn($params{corrent_loc}) ||
+ $session->editor->ws_ou;
+
+ my $method = 'open-ils.circ.checkin';
+ $method .= '.override' if $override;
+
+ my $resp = $U->simplereq(
+ 'open-ils.circ', $method, $session->editor->authtoken, $args);
+
+ # Treat the first response as the main result.
+ my $event = $resp->[0] if ref $resp eq 'ARRAY';
+
+ return unless $U->is_event($event); # should never happen; fail gracefully
+
+ my $textcode = $event->{textcode};
+ my $payload = $event->{payload} || {};
+
+ return 1 if !$override && $config->{"checkin.override.$textcode"};
+
+ my $circ = $payload->{circ};
+ my $copy = $payload->{copy};
+
+ # These may be replaced below
+ $details->{current_loc} =
+ $params{item_details}->{item}->circ_lib->shortname;
+
+ $details->{permanent_loc} =
+ $params{item_details}->{item}->circ_lib->shortname;
+
+ $details->{destination_loc} =
+ $SC->org_sn_from_id($event->{org}) if $event->{org};
+
+ if ($copy && $copy->circ_lib != $item_details->{item}->circ_lib->id) {
+ # Checkin of floating copies changes the circ lib.
+ $details->{current_loc} =
+ $details->{permanent_loc} =
+ $SC->org_sn_from_id($session, $copy->circ_lib);
+ }
+
+ $class->handle_hold($session, $details, $payload, %params);
+
+ if ($textcode eq 'NO_CHANGE' || $textcode eq 'SUCCESS') {
+
+ $details->{ok} = 1;
+
+ } elsif ($textcode eq 'ROUTE_ITEM') {
+
+ $details->{ok} = 1;
+ $details->{alert} = 1;
+ $details->{alert_type} = '04' unless $details->{alert_type};
+
+ } else {
+ $details->{ok} = 0; # unknown
+ $details->{alert} = 1;
+ $details->{alert_type} = '00' unless $details->{alert_type};
+ }
+
+ return 0;
+}
+
+sub handle_hold {
+ my ($class, $session, $details, $payload, %params) = @_;
+
+ my $hold = $payload->{remote_hold} || $payload->{hold};
+
+ return unless $hold;
+
+ my ($pickup_lib_id, $pickup_lib_sn);
+
+ my $holder = OpenILS::SIP->editor()->retrieve_actor_user(
+ [$hold->usr, {flesh => 1, flesh_fields => {au => ['card']}}]);
+
+ $details->{hold_patron_name} = $SC->format_user_name($holder);
+
+ if (my $card = $holder->card) { # null-able
+ $details->{hold_patron_barcode} = $card->barcode;
+ }
+
+ if (ref $hold->pickup_lib) {
+ $pickup_lib_id = $hold->pickup_lib->id;
+ $pickup_lib_sn = $hold->pickup_lib->shortname;
+
+ } else {
+ $pickup_lib_id = $hold->pickup_lib;
+ $pickup_lib_sn = $SC->org_sn_from_id($hold->pickup_lib);
+ }
+
+ $details->{alert} = 1;
+ $details->{destination_loc} = $pickup_lib_sn;
+ $details->{alert_type} =
+ ($pickup_lib_id == $session->editor->ws_ou) ? '01' : '02';
+}
+
+1;
return undef;
}
+my %org_sn_cache; # shortname => org
+my %org_id_cache; # id => org
+sub org_id_from_sn {
+ my ($class, $session, $org_sn) = @_;
+
+ return undef unless $org_sn;
+
+ my $org = $org_sn_cache{$org_sn} ||
+ $session->editor->search_actor_org_unit({shortname => $org_sn})->[0];
+
+ return undef unless $org;
+
+ $org_sn_cache{$org_sn} = $org;
+ $org_id_cache{$org->id} = $org;
+
+ return $org->id;
+}
+
+sub org_sn_from_id {
+ my ($class, $session, $org_id) = @_;
+
+ return undef unless $org_id;
+
+ my $org = $org_id_cache{$org_id} ||
+ $session->editor->retrieve_actor_org_unit($org_id);
+
+ return undef unless $org;
+
+ $org_sn_cache{$org->shortname} = $org;
+ $org_id_cache{$org_id} = $org;
+
+ return $org->shortname;
+}
+
# Determines which class of data the SIP client wants detailed
# information on in the patron info request.
sub patron_summary_list_items {
return '';
}
+sub format_user_name {
+ my ($class, $user) = @_;
+ return sprintf('%s%s%s',
+ $user->first_given_name ? $user->first_given_name : '',
+ $user->second_given_name ? ' ' . $user->second_given_name : '',
+ $user->family_name ? ' ' . $user->family_name : ''
+ );
+}
+
+
1;
my $details = {
item => $item,
security_marker => '02' # matches SIP/Item.pm
+ owning_loc => $item->call_number->owning_lib->sn,
+ current_loc => $item->circ_lib->shortname,
+ permanent_loc => $item->circ_lib->shortname,
+ destination_loc => $item->circ_lib->shortname # maybe replaced below
};
$details->{circ} = $e->search_action_circulation([{
$due_date->strftime('%F %T');
}
-
if ($item->status->id == OILS_COPY_STATUS_IN_TRANSIT) {
$details->{transit} = $e->search_action_transit_copy([{
target_copy => $item->id,
flesh => 1,
flesh_fields => {atc => ['dest']}
}])->[0];
+
+ $details->{destination_loc} = $details->{transit}->dest->shortname;
}
if ($item->status->id == OILS_COPY_STATUS_ON_HOLDS_SHELF || (
}])->[0];
}
-
if (my $hold = $details->{hold}) {
my $pickup_date = $hold->shelf_expire_time;
$details->{hold_pickup_date} =
my $card = $e->search_actor_card({usr => $hold->usr})->[0];
$details->{hold_patron_barcode} = $card->barcode if $card;
+ $details->{destination_loc} = $hold->pickup_lib->shortname;
}
my ($title_entry) = grep {$_->name eq 'title'}
(SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
'Checkin override copy status missing',
'checkin.override.COPY_STATUS_MISSING', 'true'
+), (
+ (SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
+ 'Checkin local holds as transits',
+ 'checkin_hold_as_transit', 'false'
);
/* EXAMPLE SETTINGS