return Apache2::Const::FORBIDDEN unless $session;
- if ($msg_code eq '63') {
- $response = handle_patron_info($session, $message);
- } elsif ($msg_code eq '17') {
+ if ($msg_code eq '17') {
$response = handle_item_info($session, $message);
+ } elsif ($msg_code eq '23') {
+ $response = handle_patron_status($session, $message);
+ } elsif ($msg_code eq '63') {
+ $response = handle_patron_info($session, $message);
}
}
return Apache2::Const::OK;
}
+
# Returns the value of the first occurrence of the requested SIP code.
sub get_field_value {
my ($message, $code) = @_;
summary_list_items => patron_summary_list_items($summary)
);
+ my $response = patron_response_common_data(
+ $session, $institution, $barcode, $password, $pdetails);
+
+ $response->{code} = '64';
+
+ return $response unless $pdetails;
+
+ push(
+ @{$response->{fixed_fields}},
+ count4($pdetails->{holds_count}),
+ count4($pdetails->{overdue_count}),
+ count4($pdetails->{out_count}),
+ count4($pdetails->{fine_count}),
+ count4($pdetails->{recall_count}),
+ count4($pdetails->{unavail_holds_count})
+ );
+
+ # TODO: Add
+ # overdue items AT variable-length optional field (this field should be sent for each overdue item).
+ # charged items AU variable-length optional field (this field should be sent for each charged item).
+ # fine items AV variable-length optional field (this field should be sent for each fine item).
+ # recall items BU variable-length optional field (this field should be sent for each recall item).
+ # unavailable hold items CD variable-length optional field (this field should be sent for each unavailable hold item).
+
+ my $list_items = patron_summary_list_items($summary) || '';
+
+ if ($list_items eq 'hold_items') {
+ for my $hold (@{$pdetails->{hold_items}}) {
+ push(@{$response->{fields}}, {AS => $hold});
+ }
+ }
+
+ return $response;
+}
+
+sub handle_patron_status {
+ my ($session, $message) = @_;
+ my $sip_account = $session->sip_account;
+
+ my $institution = get_field_value($message, 'AO');
+ my $barcode = get_field_value($message, 'AA');
+ my $password = get_field_value($message, 'AD');
+ my $instconf = get_inst_config($institution) || return undef;
+
+ my $pdetails = OpenILS::WWW::SIP2Gateway::Patron->get_patron_details(
+ session => $session,
+ instconf => $instconf,
+ barcode => $barcode,
+ password => $password
+ );
+
+ my $response = patron_response_common_data(
+ $session, $institution, $barcode, $password, $pdetails);
+
+ $response->{code} = '24';
+
+ return $response;
+}
+
+# Patron Info and Patron Status responses share mostly the same data.
+# This returns the base data which can be augmented as needed.
+# Note we don't call Patron->get_patron_details here since different
+# messages collect different amounts of data.
+sub patron_response_common_data {
+ my ($session, $institution, $barcode, $password, $pdetails) = @_;
+
if (!$pdetails) {
+ # No such user. Return a stub response with all things denied.
+
return {
- code => '64',
fixed_fields => [
spacebool(1), # charge denied
spacebool(1), # renew denied
]
};
}
-
+
return {
- code => '64',
fixed_fields => [
spacebool($pdetails->{charge_denied}),
spacebool($pdetails->{renew_denied}),
spacebool(0), # recall overdue
spacebool($pdetails->{too_many_fines}),
'000', # language
- sipdate(),
- count4($pdetails->{holds_count}),
- count4($pdetails->{overdue_count}),
- count4($pdetails->{out_count}),
- count4($pdetails->{fine_count}),
- count4($pdetails->{recall_count}),
- count4($pdetails->{unavail_holds_count}),
+ sipdate()
],
fields => [
{AO => $institution},
{AA => $barcode},
- {BL => sipbool(1)}, # valid patron
- {CQ => sipbool($password)} # password verified if exists
+ {BL => sipbool(1)}, # valid patron
+ {BV => $pdetails->{balance_owed}}, # fee amount
+ {CQ => sipbool($password)} # password verified if exists
]
};
}
-
# Determines which class of data the SIP client wants detailed
# information on in the patron info request.
sub patron_summary_list_items {
grep {$_->{id} == OILS_PENALTY_PATRON_EXCEEDS_FINES}
@$penalties;
+ my $summary = $e->retrieve_money_open_user_summary($patron->id);
+ $details->{balance_owed} = ($summary) ? $summary->balance_owed : 0;
+
set_patron_summary_items($session, $instconf, $details, %params);
+ set_patron_summary_list_items($session, $instconf, $details, %params);
return $details;
}
my ($session, $instconf, $details, %params) = @_;
my $patron = $details->{patron};
- my $start_item = $params{summary_start_item} || 0;
- my $end_item = $params{summary_end_item} || 10;
- my $list_items = $params{summary_list_items};
-
my $e = new_editor();
- my $holds_where = {
- usr => $patron->id,
- fulfillment_time => undef,
- cancel_time => undef
- };
+ $details->{recall_count} = 0; # not supported
- $holds_where->{current_shelf_lib} = {'=' => {'+ahr' => 'pickup_lib'}}
- if $instconf->{msg64_hold_items_available};
+ my $hold_ids = get_hold_ids($e, $instconf, $patron);
+ $details->{holds_count} = scalar(@$hold_ids);
- my $hold_ids = $e->json_query({
- select => {ahr => ['id']},
- from => 'ahr',
- where => {'+ahr' => $holds_where}
- });
+ my $unavail_hold_ids = get_hold_ids($e, $instconf, $patron, 1);
+ $details->{unavail_holds_count} = scalar(@$unavail_hold_ids);
- $details->{holds_count} = scalar(@$hold_ids);
$details->{overdue_count} = 0;
$details->{out_count} = 0;
$details->{out_count} = scalar(@$out_ids) + scalar(@$overdue_ids);
}
- $details->{recall_count} = undef; # not supported
-
my $xacts = $U->simplereq(
'open-ils.actor',
'open-ils.actor.user.transactions.history.have_balance',
);
$details->{fine_count} = scalar(@$xacts);
+}
+
+sub get_hold_ids {
+ my ($e, $instconf, $patron, $unavail, $offset, $limit) = @_;
+
+ my $holds_where = {
+ usr => $patron->id,
+ fulfillment_time => undef,
+ cancel_time => undef
+ };
+
+ if ($unavail) {
+ $holds_where->{'-or'} = [
+ {current_shelf_lib => undef},
+ {current_shelf_lib => {'!=' => {'+ahr' => 'pickup_lib'}}}
+ ];
+
+ } else {
+
+ $holds_where->{current_shelf_lib} = {'=' => {'+ahr' => 'pickup_lib'}}
+ if $instconf->{msg64_hold_items_available};
+ }
+
+ my $query = {
+ select => {ahr => ['id']},
+ from => 'ahr',
+ where => {'+ahr' => $holds_where}
+ };
+
+ $query->{offset} = $offset if $offset;
+ $query->{limit} = $limit if $limit;
- # TODO: unavail holds count; summary details request
+ my $id_hashes = $e->json_query($query);
+
+ return [map {$_->{id}} @$id_hashes];
+}
+
+sub set_patron_summary_list_items {
+ my ($session, $instconf, $details, %params) = @_;
+ my $e = new_editor();
+
+ my $list_items = $params{summary_list_items};
+ my $offset = $params{summary_start_item} || 0;
+ my $end_item = $params{summary_end_item} || 10;
+ my $limit = $end_item - $offset;
+
+ add_hold_items($e, $session, $instconf, $details, $offset, $limit)
+ if $list_items eq 'hold_items';
}
+sub add_hold_items {
+ my ($e, $session, $instconf, $details, $offset, $limit) = @_;
+
+ my $patron = $details->{patron};
+ my $format = $instconf->{msg64_hold_datatype} || '';
+ my $hold_ids = get_hold_ids($e, $instconf, $patron, 0, $offset, $limit);
+
+ my @hold_items;
+ for my $hold_id (@$hold_ids) {
+ my $hold = $e->retrieve_action_hold_request($hold_id);
+
+ if ($format eq 'barcode') {
+ my $copy = find_copy_for_hold($e, $hold);
+ push(@hold_items, $copy->barcode) if $copy;
+ } else {
+ my $title = find_title_for_hold($e, $hold);
+ push(@hold_items, $title) if $title;
+ }
+ }
+
+ $details->{hold_items} = \@hold_items;
+}
+
+# Hold -> reporter.hold_request_record -> display field for title.
+sub find_title_for_hold {
+ my ($e, $hold) = @_;
+
+ my $bib_link = $e->retrieve_reporter_hold_request_record($hold->id);
+
+ my $title_field = $e->search_metabib_flat_display_entry({
+ source => $bib_link->bib_record, name => 'title'})->[0];
+
+ return $title_field ? $title_field->value : '';
+}
+
+# Finds a representative copy for the given hold. If no copy exists at
+# all, undef is returned. The only limit placed on what constitutes a
+# "representative" copy is that it cannot be deleted. Otherwise, any
+# copy that allows us to find the hold later is good enough.
+sub find_copy_for_hold {
+ my ($e, $hold) = @_;
+
+ return $e->retrieve_asset_copy($hold->current_copy)
+ if $hold->current_copy;
+
+ return $e->retrieve_asset_copy($hold->target)
+ if $hold->hold_type =~ /C|R|F/;
+
+ return $e->search_asset_copy([
+ {call_number => $hold->target, deleted => 'f'},
+ {limit => 1}])->[0] if $hold->hold_type eq 'V';
+
+ my $bre_ids = [$hold->target];
+
+ if ($hold->hold_type eq 'M') {
+ # find all of the bibs that link to the target metarecord
+ my $maps = $e->search_metabib_metarecord_source_map(
+ {metarecord => $hold->target});
+ $bre_ids = [map {$_->record} @$maps];
+ }
+
+ my $vol_ids = $e->search_asset_call_number(
+ {record => $bre_ids, deleted => 'f'},
+ {idlist => 1}
+ );
+
+ return $e->search_asset_copy([
+ {call_number => $vol_ids, deleted => 'f'},
+ {limit => 1}
+ ])->[0];
+}
+
+
sub set_patron_privileges {
my ($session, $instconf, $details, $penalties) = @_;
my $patron = $details->{patron};
->parse_datetime(clean_ISO8601($patron->expire_date));
if ($expire < DateTime->now) {
- $logger->info(
- "SIP2 Patron account is expired; all privileges blocked");
+ $logger->info("SIP2 Patron account is expired; all privileges blocked");
$details->{charge_denied} = 1;
$details->{recall_denied} = 1;
$details->{renew_denied} = 1;