</permacrud>
</class>
- <class id="csa" controller="open-ils.cstore open-ils.pcrud"
- oils_obj:fieldmapper="config::sip_account"
- oils_persist:tablename="config.sip_account"
- reporter:label="SIP AccountS">
- <fields oils_persist:primary="id" oils_persist:sequence="config.sip_account_id_seq">
+ <class id="cssg" controller="open-ils.cstore open-ils.pcrud"
+ oils_obj:fieldmapper="config::sip_setting_group"
+ oils_persist:tablename="config.sip_setting_group"
+ reporter:label="SIP Settings Group">
+ <fields oils_persist:primary="id" oils_persist:sequence="config.sip_setting_id_seq">
<field name="id" reporter:datatype="id" reporter:label="ID" reporter:selector="sip_username"/>
- <field name="enabled" reporter:datatype="bool" reporter:label="Enabled"/>
- <field name="institution" reporter:datatype="text" reporter:label="Institution" oils_obj:required="true"/>
- <field name="sip_username" reporter:datatype="text" reporter:label="SIP Username" oils_obj:required="true"/>
- <field name="usr" reporter:datatype="link" reporter:label="ILS User" oils_obj:required="true"/>
- <field name="workstation" reporter:datatype="link" reporter:label="Workstation"/>
- <field name="av_format" reporter:datatype="text" reporter:label="SIP AV Format"/>
+ <field name="label" reporter:datatype="text" reporter:label="Label" oils_obj:required="true"/>
+ <field name="institution" reporter:datatype="text" reporter:label="SIP Institution" oils_obj:required="true"/>
+ <field name="settings" reporter:datatype="link" reporter:label="Settings" oils_persist:virtual="true"/>
</fields>
<links>
- <link field="usr" reltype="has_a" key="id" map="" class="au"/>
- <link field="workstation" reltype="has_a" key="id" map="" class="aws"/>
+ <link field="settings" reltype="has_many" key="setting_group" map="" class="css"/>
</links>
<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
<actions>
</actions>
</permacrud>
</class>
-
<class id="css" controller="open-ils.cstore open-ils.pcrud"
oils_obj:fieldmapper="config::sip_setting"
oils_persist:tablename="config.sip_setting"
reporter:label="SIP Settings">
<fields oils_persist:primary="id" oils_persist:sequence="config.sip_setting_id_seq">
<field name="id" reporter:datatype="id" reporter:label="ID" reporter:selector="sip_username"/>
- <field name="institution" reporter:datatype="text" reporter:label="Institution" oils_obj:required="true"/>
- <field name="name" reporter:datatype="text" reporter:label="Institution" oils_obj:required="true"/>
- <field name="value" reporter:datatype="text" reporter:label="Institution" oils_obj:required="true"/>
+ <field name="setting_group" reporter:datatype="link" reporter:label="Settings Group" oils_obj:required="true"/>
+ <field name="name" reporter:datatype="text" reporter:label="Name" oils_obj:required="true"/>
+ <field name="description" reporter:datatype="text" reporter:label="Description" oils_obj:required="true"/>
+ <field name="value" reporter:datatype="text" reporter:label="Value" oils_obj:required="true"/>
</fields>
+ <links>
+ <link field="setting_group" reltype="has_a" key="id" map="" class="cssg"/>
+ </links>
<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
<actions>
<create permission="SIP_ADMIN" global_required="true"/>
</actions>
</permacrud>
</class>
+ <class id="csa" controller="open-ils.cstore open-ils.pcrud"
+ oils_obj:fieldmapper="config::sip_account"
+ oils_persist:tablename="config.sip_account"
+ reporter:label="SIP AccountS">
+ <fields oils_persist:primary="id" oils_persist:sequence="config.sip_account_id_seq">
+ <field name="id" reporter:datatype="id" reporter:label="ID" reporter:selector="sip_username"/>
+ <field name="enabled" reporter:datatype="bool" reporter:label="Enabled"/>
+ <field name="setting_group" reporter:datatype="link" reporter:label="Settings Group" oils_obj:required="true"/>
+ <field name="sip_username" reporter:datatype="text" reporter:label="SIP Username" oils_obj:required="true"/>
+ <field name="usr" reporter:datatype="link" reporter:label="ILS User" oils_obj:required="true"/>
+ <field name="workstation" reporter:datatype="link" reporter:label="Workstation"/>
+ <field name="av_format" reporter:datatype="text" reporter:label="SIP AV Format"/>
+ </fields>
+ <links>
+ <link field="usr" reltype="has_a" key="id" map="" class="au"/>
+ <link field="workstation" reltype="has_a" key="id" map="" class="aws"/>
+ <link field="setting_group" reltype="has_a" key="id" map="" class="cssg"/>
+ </links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <create permission="SIP_ADMIN" global_required="true"/>
+ <retrieve permission="SIP_ADMIN" global_required="true"/>
+ <update permission="SIP_ADMIN" global_required="true"/>
+ <delete permission="SIP_ADMIN" global_required="true"/>
+ </actions>
+ </permacrud>
+ </class>
+
+
<!-- ********************************************************************************************************************* -->
</IDL>
my $barcode = $SC->get_field_value($message, 'AB');
my $config = $session->config;
- my $idetails = OpenILS::Application::SIP2::Item->get_item_details(
+ my $details = OpenILS::Application::SIP2::Item->get_item_details(
$session, barcode => $barcode
);
- if (!$idetails) {
+ if (!$details) {
# No matching item found, return a minimal response.
return {
code => '18',
return {
code => '18',
fixed_fields => [
- $idetails->{circ_status},
+ $details->{circ_status},
'02', # Security Marker, consistent with ../SIP*
- $idetails->{fee_type},
+ $details->{fee_type},
$SC->sipdate
],
fields => [
{AB => $barcode},
- {AH => $idetails->{due_date}},
- {AJ => $idetails->{title}},
- {AP => $idetails->{item}->circ_lib->shortname},
- {AQ => $idetails->{item}->circ_lib->shortname},
- {BG => $idetails->{item}->circ_lib->shortname},
+ {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},
{BH => $config->{settings}->{currency}},
- {BV => $idetails->{item}->deposit_amount},
- {CF => $idetails->{hold_queue_length}},
- {CK => $idetails->{media_type}},
- {CM => $idetails->{hold_pickup_date}}
+ {BV => $details->{item}->deposit_amount},
+ {CF => $details->{hold_queue_length}},
+ {CK => $details->{media_type}},
+ {CM => $details->{hold_pickup_date}},
+ {CY => $details->{hold_patron_barcode}}
]
};
}
my $list_items = $SC->patron_summary_list_items($summary);
- my $pdetails = OpenILS::Application::SIP2::Patron->get_patron_details(
+ my $details = OpenILS::Application::SIP2::Patron->get_patron_details(
$session,
barcode => $barcode,
password => $password,
);
my $response =
- patron_response_common_data($session, $barcode, $password, $pdetails);
+ patron_response_common_data($session, $barcode, $password, $details);
$response->{code} = '64';
- return $response unless $pdetails;
- my $patron = $pdetails->{patron};
+ return $response unless $details;
+ my $patron = $details->{patron};
push(
@{$response->{fixed_fields}},
- $SC->count4($pdetails->{holds_count}),
- $SC->count4($pdetails->{overdue_count}),
- $SC->count4($pdetails->{out_count}),
- $SC->count4($pdetails->{fine_count}),
- $SC->count4($pdetails->{recall_count}),
- $SC->count4($pdetails->{unavail_holds_count})
+ $SC->count4($details->{holds_count}),
+ $SC->count4($details->{overdue_count}),
+ $SC->count4($details->{out_count}),
+ $SC->count4($details->{fine_count}),
+ $SC->count4($details->{recall_count}),
+ $SC->count4($details->{unavail_holds_count})
);
push(
);
if ($list_items eq 'hold_items') {
- for my $hold (@{$pdetails->{hold_items}}) {
+ for my $hold (@{$details->{hold_items}}) {
push(@{$response->{fields}}, {AS => $hold});
}
} elsif ($list_items eq 'charged_items') {
- for my $item (@{$pdetails->{items_out}}) {
+ for my $item (@{$details->{items_out}}) {
push(@{$response->{fields}}, {AU => $item});
}
} elsif ($list_items eq 'overdue_items') {
- for my $item (@{$pdetails->{overdue_items}}) {
+ for my $item (@{$details->{overdue_items}}) {
push(@{$response->{fields}}, {AT => $item});
}
} elsif ($list_items eq 'fine_items') {
- for my $item (@{$pdetails->{fine_items}}) {
+ for my $item (@{$details->{fine_items}}) {
push(@{$response->{fields}}, {AV => $item});
}
} elsif ($list_items eq 'unavailable_holds') {
- for my $item (@{$pdetails->{unavailable_holds}}) {
+ for my $item (@{$details->{unavailable_holds}}) {
push(@{$response->{fields}}, {CD => $item});
}
}
my $barcode = $SC->get_field_value($message, 'AA');
my $password = $SC->get_field_value($message, 'AD');
- my $pdetails = OpenILS::Application::SIP2::Patron->get_patron_details(
+ my $details = OpenILS::Application::SIP2::Patron->get_patron_details(
$session,
barcode => $barcode,
password => $password
);
my $response = patron_response_common_data(
- $session, $barcode, $password, $pdetails);
+ $session, $barcode, $password, $details);
$response->{code} = '24';
# 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, $barcode, $password, $pdetails) = @_;
+ my ($session, $barcode, $password, $details) = @_;
- if (!$pdetails) {
+ if (!$details) {
# No such user. Return a stub response with all things denied.
return {
};
}
- my $patron = $pdetails->{patron};
+ my $patron = $details->{patron};
return {
fixed_fields => [
- $SC->spacebool($pdetails->{charge_denied}),
- $SC->spacebool($pdetails->{renew_denied}),
- $SC->spacebool($pdetails->{recall_denied}),
- $SC->spacebool($pdetails->{holds_denied}),
+ $SC->spacebool($details->{charge_denied}),
+ $SC->spacebool($details->{renew_denied}),
+ $SC->spacebool($details->{recall_denied}),
+ $SC->spacebool($details->{holds_denied}),
$SC->spacebool($patron->card->active eq 'f'),
$SC->spacebool(0), # too many charged
- $SC->spacebool($pdetails->{too_may_overdue}),
+ $SC->spacebool($details->{too_may_overdue}),
$SC->spacebool(0), # too many renewals
$SC->spacebool(0), # too many claims retruned
$SC->spacebool(0), # too many lost
- $SC->spacebool($pdetails->{too_many_fines}),
- $SC->spacebool($pdetails->{too_many_fines}),
+ $SC->spacebool($details->{too_many_fines}),
+ $SC->spacebool($details->{too_many_fines}),
$SC->spacebool(0), # recall overdue
- $SC->spacebool($pdetails->{too_many_fines}),
+ $SC->spacebool($details->{too_many_fines}),
'000', # language
$SC->sipdate
],
fields => [
- {AO => $session->config->{institution}},
{AA => $barcode},
- {BL => $SC->sipbool(1)}, # valid patron
- {BV => $pdetails->{balance_owed}}, # fee amount
- {CQ => $SC->sipbool($password)} # password verified if exists
+ {AO => $session->config->{institution}},
+ {BH => $session->config->{settings}->{currency}},
+ {BL => $SC->sipbool(1)}, # valid patron
+ {BV => $details->{balance_owed}}, # fee amount
+ {CQ => $SC->sipbool($password)} # password verified if exists
]
};
}
}
- if ($details->{hold}) {
- my $pickup_date = $details->{hold}->shelf_expire_time;
+ if (my $hold = $details->{hold}) {
+ my $pickup_date = $hold->shelf_expire_time;
$details->{hold_pickup_date} =
$pickup_date ? $SC->sipdate($pickup_date) : undef;
+
+ my $card = $e->search_actor_card({usr => $hold->usr})->[0];
+ $details->{hold_patron_barcode} = $card->barcode if $card;
}
my ($title_entry) = grep {$_->name eq 'title'}
# Supported Messages (BX)
# Currently hard-coded, since it's based on availabilty of functionality
# in the code, but it could be moved into the database to limit access for
-# specific institutions.
+# specific setting groups.
use constant INSTITUTION_SUPPORTS => [
'Y', # patron status request,
'Y', # checkout,
my $self = shift;
return $self->{config} if $self->{config};
- my $inst = $self->sip_account->institution;
+ my $group = $self->editor->retrieve_config_sip_setting_group([
+ $self->sip_account->setting_group,
+ {flesh => 1, flesh_fields => {cssg => ['settings']}}
+ ]);
my $config = {
- institution => $inst,
- settings => {
- currency => 'USD' # TODO add db setting
- },
+ institution => $group->institution,
supports => INSTITUTION_SUPPORTS
};
- # Institution "*" provides default values for all institution configs.
- my $settings =
- $self->editor->search_config_sip_setting({institution => ['*', $inst]});
-
- # Institution specific settings.
- for my $set (grep {$_->institution eq $inst} @$settings) {
- $config->{settings}->{$set->name} = $json->decode($set->value);
- }
-
- # Apply values for global settings without replacing
- # institution-specific values.
- for my $set (grep {$_->institution eq '*'} @$settings) {
- my $name = $set->name;
- my $value = $json->decode($set->value);
-
- $config->{settings}->{$name} = $value
- unless exists $config->{settings}->{$name};
- }
+ # Decode and hashify settings for easy access
+ $config->{settings} =
+ {map {$_->name => $json->decode($_->value)} @{$group->settings}};
+ $logger->info("SIP settings " . $json->encode($config->{settings}));
return $self->{config} = $config;
}
-- SELECT evergreen.upgrade_deps_block_check('TODO', :eg_version);
+-- Collections of settings that can be linked to one or more SIP accounts.
+CREATE TABLE config.sip_setting_group (
+ id SERIAL PRIMARY KEY,
+ label TEXT UNIQUE NOT NULL,
+ institution TEXT NOT NULL -- Duplicates OK
+);
+
+-- Key/value setting pairs
+CREATE TABLE config.sip_setting (
+ id SERIAL PRIMARY KEY,
+ setting_group INTEGER NOT NULL REFERENCES config.sip_setting_group (id)
+ ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ name TEXT NOT NULL,
+ description TEXT NOT NULL,
+ value JSON NOT NULL,
+ CONSTRAINT name_once_per_inst UNIQUE (setting_group, name)
+);
+
CREATE TABLE config.sip_account (
id SERIAL PRIMARY KEY,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
- institution TEXT NOT NULL,
+ setting_group INTEGER NOT NULL REFERENCES config.sip_setting_group (id)
+ DEFERRABLE INITIALLY DEFERRED,
sip_username TEXT NOT NULL,
sip_password BIGINT NOT NULL REFERENCES actor.passwd
DEFERRABLE INITIALLY DEFERRED,
av_format TEXT -- e.g. '3m'
);
--- institution and global-level key/value setting pairs.
-CREATE TABLE config.sip_setting (
- id SERIAL PRIMARY KEY,
- institution TEXT NOT NULL, -- '*' applies to all institutions
- name TEXT NOT NULL,
- value JSON NOT NULL,
- CONSTRAINT name_once_per_inst UNIQUE (institution, name)
-);
-
-- SEED DATA
INSERT INTO actor.passwd_type (code, name, login, crypt_algo, iter_count)
VALUES ('sip2', 'SIP2 Client Password', FALSE, 'bf', 5);
-INSERT INTO config.sip_setting (institution, name, value)
-VALUES
- ('*', 'allow_sc_status_before_login', 'true'),
- ('*', 'currency', '"USD"'),
- ('*', 'due_date_use_sip_date_format', 'false'),
- ('*', 'patron_status_permit_loans', 'false'),
- ('*', 'patron_status_permit_all', 'false'),
- ('*', 'msg64_summary_datatype', '"title"'),
- ('*', 'msg64_hold_items_available', '"title"')
-;
+INSERT INTO config.sip_setting_group (label, institution)
+ VALUES ('Example Setting Group', 'example');
+
+INSERT INTO config.sip_setting (setting_group, description, name, value)
+VALUES (
+ (SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
+ 'Monetary amounts are reported in this currency',
+ 'currency', '"USD"'
+), (
+ (SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
+ 'Allow clients to request the SIP server status before login (message 99)',
+ 'allow_sc_status_before_login', 'true'
+), (
+ (SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
+ 'Due date uses 18-char date format (YYYYMMDDZZZZHHMMSS). Otherwise "YYYY-MM-DD HH:MM:SS',
+ 'due_date_use_sip_date_format', 'false'
+), (
+ (SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
+ 'Checkout and renewal are allowed even when penalties blocking these actions exist',
+ 'patron_status_permit_loans', 'false'
+), (
+ (SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
+ 'Holds, checkouts, and renewals allowed regardless of blocking penalties',
+ 'patron_status_permit_all', 'false'
+), (
+ (SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
+ 'Patron circulation data may be returned as either "title" or "barcode"',
+ 'msg64_summary_datatype', '"title"'
+), (
+ (SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
+ 'Patron holds data may be returned as either "title" or "barcode"',
+ 'msg64_hold_items_available', '"title"'
+);
/* EXAMPLE SETTINGS
+
-- Example linking a SIP password to the 'admin' account.
SELECT actor.set_passwd(1, 'sip2', 'sip_password');
INSERT INTO actor.workstation (name, owning_lib) VALUES ('BR1-SIP2-Gateway', 4);
INSERT INTO config.sip_account(
- institution, sip_username, sip_password, usr, workstation, av_format
+ setting_group, sip_username, sip_password, usr, workstation, av_format
) VALUES (
- 'example', 'admin',
+ (SELECT id FROM config.sip_setting_group WHERE institution = 'example'),
+ 'admin',
(SELECT id FROM actor.passwd WHERE usr = 1 AND passwd_type = 'sip2'),
1,
(SELECT id FROM actor.workstation WHERE name = 'BR1-SIP2-Gateway'),