[open-ils-commits] r17532 - in branches/rel_2_0/Open-ILS/src: perlmods/OpenILS perlmods/OpenILS/Application/Circ sql/Pg sql/Pg/upgrade (miker)
svn at svn.open-ils.org
svn at svn.open-ils.org
Wed Sep 8 22:10:09 EDT 2010
Author: miker
Date: 2010-09-08 22:10:07 -0400 (Wed, 08 Sep 2010)
New Revision: 17532
Added:
branches/rel_2_0/Open-ILS/src/sql/Pg/upgrade/0389.data.issuance-holds.sql
Modified:
branches/rel_2_0/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
branches/rel_2_0/Open-ILS/src/perlmods/OpenILS/Const.pm
branches/rel_2_0/Open-ILS/src/sql/Pg/002.schema.config.sql
branches/rel_2_0/Open-ILS/src/sql/Pg/950.data.seed-values.sql
Log:
Merging r17527-17529 from trunk: Backend issuance-level hold logic
Modified: branches/rel_2_0/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
===================================================================
--- branches/rel_2_0/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm 2010-09-09 01:51:23 UTC (rev 17531)
+++ branches/rel_2_0/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm 2010-09-09 02:10:07 UTC (rev 17532)
@@ -177,8 +177,14 @@
return $e->event unless $e->allowed('TITLE_HOLDS', $porg);
} elsif ( $t eq OILS_HOLD_TYPE_VOLUME ) {
return $e->event unless $e->allowed('VOLUME_HOLDS', $porg);
+ } elsif ( $t eq OILS_HOLD_TYPE_ISSUANCE ) {
+ return $e->event unless $e->allowed('ISSUANCE_HOLDS', $porg);
} elsif ( $t eq OILS_HOLD_TYPE_COPY ) {
return $e->event unless $e->allowed('COPY_HOLDS', $porg);
+ } elsif ( $t eq OILS_HOLD_TYPE_FORCE ) {
+ return $e->event unless $e->allowed('COPY_HOLDS', $porg);
+ } elsif ( $t eq OILS_HOLD_TYPE_RECALL ) {
+ return $e->event unless $e->allowed('COPY_HOLDS', $porg);
}
if( @events ) {
@@ -1526,7 +1532,7 @@
Returns a list ids of un-fulfilled holds for a given title id
@param authtoken The login session key
@param id the id of the item whose holds we want to retrieve
- @param type The hold type - M, T, V, C
+ @param type The hold type - M, T, I, V, C, F, R
/
);
@@ -1739,11 +1745,12 @@
depth - hold range depth (default 0)
pickup_lib - destination for hold, fallback value for selection_ou
selection_ou - ID of org_unit establishing hard and soft hold boundary settings
+ issuanceid - ID of the issuance to be held, required for Issuance level hold
titleid - ID (BRN) of the title to be held, required for Title level hold
volume_id - required for Volume level hold
copy_id - required for Copy level hold
mrid - required for Meta-record level hold
- hold_type - T,C,V or M for Title, Copy, Volume or Meta-record (default "T")
+ hold_type - T, C (or R or F), I, V or M for Title, Copy, Issuance, Volume or Meta-record (default "T")
All key/value pairs are passed on to do_possibility_checks.
@@ -1829,6 +1836,7 @@
sub do_possibility_checks {
my($e, $patron, $request_lib, $depth, %params) = @_;
+ my $issuanceid = $params{issuance} || "";
my $titleid = $params{titleid} || "";
my $volid = $params{volume_id};
my $copyid = $params{copy_id};
@@ -1842,7 +1850,7 @@
my $volume;
my $title;
- if( $hold_type eq OILS_HOLD_TYPE_COPY ) {
+ if( $hold_type eq OILS_HOLD_TYPE_FORCE || $hold_type eq OILS_HOLD_TYPE_RECALL || $hold_type eq OILS_HOLD_TYPE_COPY ) {
return $e->event unless $copy = $e->retrieve_asset_copy($copyid);
return $e->event unless $volume = $e->retrieve_asset_call_number($copy->call_number);
@@ -1867,6 +1875,12 @@
$titleid, $depth, $request_lib, $patron, $e->requestor, $pickup_lib, $selection_ou
);
+ } elsif( $hold_type eq OILS_HOLD_TYPE_ISSUANCE ) {
+
+ return _check_issuance_hold_is_possible(
+ $issuanceid, $depth, $request_lib, $patron, $e->requestor, $pickup_lib, $selection_ou
+ );
+
} elsif( $hold_type eq OILS_HOLD_TYPE_METARECORD ) {
my $maps = $e->search_metabib_metarecord_source_map({metarecord=>$mrid});
@@ -2016,7 +2030,7 @@
unless($title) { # grab the title if we don't already have it
my $vol = $e->retrieve_asset_call_number(
- [ $copy->call_number, { flesh => 1, flesh_fields => { acn => ['record'] } } ] );
+ [ $copy->call_number, { flesh => 1, flesh_fields => { bre => ['fixed_fields'], acn => ['record'] } } ] );
$title = $vol->record;
}
@@ -2030,7 +2044,141 @@
return @status;
}
+sub _check_issuance_hold_is_possible {
+ my( $issuanceid, $depth, $request_lib, $patron, $requestor, $pickup_lib, $selection_ou ) = @_;
+
+ my $e = new_editor();
+ my %org_filter = create_ranged_org_filter($e, $selection_ou, $depth);
+ # this monster will grab the id and circ_lib of all of the "holdable" copies for the given record
+ my $copies = $e->json_query(
+ {
+ select => { acp => ['id', 'circ_lib'] },
+ from => {
+ acp => {
+ sitem => {
+ field => 'unit',
+ fkey => 'id',
+ filter => { issuance => $issuanceid }
+ },
+ acpl => { field => 'id', filter => { holdable => 't'}, fkey => 'location' },
+ ccs => { field => 'id', filter => { holdable => 't'}, fkey => 'status' }
+ }
+ },
+ where => {
+ '+acp' => { circulate => 't', deleted => 'f', holdable => 't', %org_filter }
+ },
+ distinct => 1
+ }
+ );
+
+ $logger->info("issuance possible found ".scalar(@$copies)." potential copies");
+
+ my $empty_ok;
+ if (!@$copies) {
+ $empty_ok = $e->retrieve_config_global_flag('circ.holds.empty_issuance_ok');
+ $empty_ok = ($empty_ok and $U->is_true($empty_ok->enabled));
+
+ return (
+ 0, 0, [
+ new OpenILS::Event(
+ "HIGH_LEVEL_HOLD_HAS_NO_COPIES",
+ "payload" => {"fail_part" => "no_ultimate_items"}
+ )
+ ]
+ ) unless $empty_ok;
+
+ return (1, 0);
+ }
+
+ # -----------------------------------------------------------------------
+ # sort the copies into buckets based on their circ_lib proximity to
+ # the patron's home_ou.
+ # -----------------------------------------------------------------------
+
+ my $home_org = $patron->home_ou;
+ my $req_org = $request_lib->id;
+
+ $logger->info("prox cache $home_org " . $prox_cache{$home_org});
+
+ $prox_cache{$home_org} =
+ $e->search_actor_org_unit_proximity({from_org => $home_org})
+ unless $prox_cache{$home_org};
+ my $home_prox = $prox_cache{$home_org};
+
+ my %buckets;
+ my %hash = map { ($_->to_org => $_->prox) } @$home_prox;
+ push( @{$buckets{ $hash{$_->{circ_lib}} } }, $_->{id} ) for @$copies;
+
+ my @keys = sort { $a <=> $b } keys %buckets;
+
+
+ if( $home_org ne $req_org ) {
+ # -----------------------------------------------------------------------
+ # shove the copies close to the request_lib into the primary buckets
+ # directly before the farthest away copies. That way, they are not
+ # given priority, but they are checked before the farthest copies.
+ # -----------------------------------------------------------------------
+ $prox_cache{$req_org} =
+ $e->search_actor_org_unit_proximity({from_org => $req_org})
+ unless $prox_cache{$req_org};
+ my $req_prox = $prox_cache{$req_org};
+
+ my %buckets2;
+ my %hash2 = map { ($_->to_org => $_->prox) } @$req_prox;
+ push( @{$buckets2{ $hash2{$_->{circ_lib}} } }, $_->{id} ) for @$copies;
+
+ my $highest_key = $keys[@keys - 1]; # the farthest prox in the exising buckets
+ my $new_key = $highest_key - 0.5; # right before the farthest prox
+ my @keys2 = sort { $a <=> $b } keys %buckets2;
+ for my $key (@keys2) {
+ last if $key >= $highest_key;
+ push( @{$buckets{$new_key}}, $_ ) for @{$buckets2{$key}};
+ }
+ }
+
+ @keys = sort { $a <=> $b } keys %buckets;
+
+ my $title;
+ my %seen;
+ my @status;
+ OUTER: for my $key (@keys) {
+ my @cps = @{$buckets{$key}};
+
+ $logger->info("looking at " . scalar(@{$buckets{$key}}). " copies in proximity bucket $key");
+
+ for my $copyid (@cps) {
+
+ next if $seen{$copyid};
+ $seen{$copyid} = 1; # there could be dupes given the merged buckets
+ my $copy = $e->retrieve_asset_copy($copyid);
+ $logger->debug("looking at bucket_key=$key, copy $copyid : circ_lib = " . $copy->circ_lib);
+
+ unless($title) { # grab the title if we don't already have it
+ my $vol = $e->retrieve_asset_call_number(
+ [ $copy->call_number, { flesh => 1, flesh_fields => { bre => ['fixed_fields'], acn => ['record'] } } ] );
+ $title = $vol->record;
+ }
+
+ @status = verify_copy_for_hold(
+ $patron, $requestor, $title, $copy, $pickup_lib, $request_lib);
+
+ last OUTER if $status[0];
+ }
+ }
+
+ if (!$status[0]) {
+ if (!defined($empty_ok)) {
+ $empty_ok = $e->retrieve_config_global_flag('circ.holds.empty_issuance_ok');
+ $empty_ok = ($empty_ok and $U->is_true($empty_ok->enabled));
+ }
+
+ return (1,0) if ($empty_ok);
+ }
+ return @status;
+}
+
+
sub _check_volume_hold_is_possible {
my( $vol, $title, $depth, $request_lib, $patron, $requestor, $pickup_lib, $selection_ou ) = @_;
my %org_filter = create_ranged_org_filter(new_editor(), $selection_ou, $depth);
@@ -2678,14 +2826,22 @@
limit => 1
};
- if($hold_type eq 'C') {
+ if($hold_type eq 'C' || $hold_type eq 'R' || $hold_type eq 'F') {
$query->{where}->{'+acp'}->{id}->{in}->{where}->{'target_copy'} = $hold_target;
} elsif($hold_type eq 'V') {
$query->{where}->{'+acp'}->{call_number} = $hold_target;
-
+
+ } elsif($hold_type eq 'I') {
+
+ $query->{from}->{acp}->{sitem} = {
+ field => 'unit',
+ fkey => 'id',
+ filter => {issuance => $hold_target},
+ };
+
} elsif($hold_type eq 'T') {
$query->{from}->{acp}->{acn} = {
Modified: branches/rel_2_0/Open-ILS/src/perlmods/OpenILS/Const.pm
===================================================================
--- branches/rel_2_0/Open-ILS/src/perlmods/OpenILS/Const.pm 2010-09-09 01:51:23 UTC (rev 17531)
+++ branches/rel_2_0/Open-ILS/src/perlmods/OpenILS/Const.pm 2010-09-09 02:10:07 UTC (rev 17532)
@@ -96,6 +96,9 @@
econst OILS_HOLD_TYPE_COPY => 'C';
+econst OILS_HOLD_TYPE_FORCE => 'F';
+econst OILS_HOLD_TYPE_RECALL => 'R';
+econst OILS_HOLD_TYPE_ISSUANCE => 'I';
econst OILS_HOLD_TYPE_VOLUME => 'V';
econst OILS_HOLD_TYPE_TITLE => 'T';
econst OILS_HOLD_TYPE_METARECORD => 'M';
Modified: branches/rel_2_0/Open-ILS/src/sql/Pg/002.schema.config.sql
===================================================================
--- branches/rel_2_0/Open-ILS/src/sql/Pg/002.schema.config.sql 2010-09-09 01:51:23 UTC (rev 17531)
+++ branches/rel_2_0/Open-ILS/src/sql/Pg/002.schema.config.sql 2010-09-09 02:10:07 UTC (rev 17532)
@@ -68,7 +68,7 @@
install_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-INSERT INTO config.upgrade_log (version) VALUES ('0388'); -- phasefx
+INSERT INTO config.upgrade_log (version) VALUES ('0389'); -- miker
CREATE TABLE config.bib_source (
id SERIAL PRIMARY KEY,
Modified: branches/rel_2_0/Open-ILS/src/sql/Pg/950.data.seed-values.sql
===================================================================
--- branches/rel_2_0/Open-ILS/src/sql/Pg/950.data.seed-values.sql 2010-09-09 01:51:23 UTC (rev 17531)
+++ branches/rel_2_0/Open-ILS/src/sql/Pg/950.data.seed-values.sql 2010-09-09 02:10:07 UTC (rev 17532)
@@ -1382,8 +1382,10 @@
,(391, 'UPDATE_PICKUP_LIB_FROM_TRANSIT', oils_i18n_gettext( 391, 'Allow a user to change the pickup and transit destination for a captured hold item already in transit', 'ppl', 'description' ))
,(392, 'COPY_NEEDED_FOR_HOLD.override', oils_i18n_gettext( 392, 'Allow a user to force renewal of an item that could fulfill a hold request', 'ppl', 'description' ))
,(393, 'MERGE_AUTH_RECORDS', oils_i18n_gettext( 393, 'Allow a user to merge authority records together', 'ppl', 'description' ))
+ ,(394, 'ISSUANCE_HOLDS', oils_i18n_gettext( 394, 'Allow a user to place holds on serials issuances', 'ppl', 'description' ))
;
+
SELECT SETVAL('permission.perm_list_id_seq'::TEXT, 1000);
INSERT INTO permission.grp_tree (id, name, parent, description, perm_interval, usergroup, application_perm) VALUES
@@ -6220,6 +6222,18 @@
'bool'
);
+INSERT INTO config.global_flag (name, label, enabled)
+ VALUES (
+ 'circ.holds.empty_issuance_ok',
+ oils_i18n_gettext(
+ 'circ.holds.empty_issuance_ok',
+ 'Holds: Allow holds on empty issuances',
+ 'cgf',
+ 'label'
+ ),
+ TRUE
+ );
+
INSERT INTO config.global_flag (name, label) -- defaults to enabled=FALSE
VALUES (
'ingest.disable_authority_linking',
Copied: branches/rel_2_0/Open-ILS/src/sql/Pg/upgrade/0389.data.issuance-holds.sql (from rev 17527, trunk/Open-ILS/src/sql/Pg/upgrade/0389.data.issuance-holds.sql)
===================================================================
--- branches/rel_2_0/Open-ILS/src/sql/Pg/upgrade/0389.data.issuance-holds.sql (rev 0)
+++ branches/rel_2_0/Open-ILS/src/sql/Pg/upgrade/0389.data.issuance-holds.sql 2010-09-09 02:10:07 UTC (rev 17532)
@@ -0,0 +1,21 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0389'); -- miker
+
+-- Making this a global_flag (UI accessible) instead of an internal_flag
+INSERT INTO config.global_flag (name, label, enabled)
+ VALUES (
+ 'circ.holds.empty_issuance_ok',
+ oils_i18n_gettext(
+ 'circ.holds.empty_issuance_ok',
+ 'Holds: Allow holds on empty issuances',
+ 'cgf',
+ 'label'
+ ),
+ TRUE
+ );
+
+INSERT INTO permission.perm_list (code, description) VALUES ('ISSUANCE_HOLDS', 'Allow a user to place holds on serials issuances');
+
+COMMIT;
+
More information about the open-ils-commits
mailing list