From: miker Date: Thu, 21 Jul 2005 22:35:28 +0000 (+0000) Subject: moving the hold copy targeter into the storage server (speed and convenience) X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=1ba0de9ea0ae4cb5ec24e6483561939a9652c8f7;p=Evergreen.git moving the hold copy targeter into the storage server (speed and convenience) git-svn-id: svn://svn.open-ils.org/ILS/trunk@1353 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm index 97f25571a5..fe69771114 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm @@ -1,6 +1,13 @@ package OpenILS::Application::Storage::Publisher::action; use base qw/OpenILS::Application::Storage/; use OpenSRF::Utils::Logger qw/:level/; +use OpenSRF::Utils qw/:datetime/; +use OpenSRF::EX qw/:try/; +use OpenILS::Utils::Fieldmapper; +use DateTime; +use DateTime::Format::ISO8601; + +my $parser = DateTime::Format::ISO8601->new; my $log = 'OpenSRF::Utils::Logger'; sub grab_overdue { @@ -253,4 +260,411 @@ __PACKAGE__->register_method( ); +sub generate_fines { + my $self = shift; + my $client = shift; + my $grace = shift; + my $circ = shift; + + + my @circs; + if ($circ) { + push @circs, + $self->method_lookup( + 'open-ils.storage.direct.action.circulation.search_where' + )->run( { id => $circ, stop_fines => undef } ); + } else { + push @circs, $self->method_lookup('open-ils.storage.action.circulation.overdue')->run( $grace ); + } + + for my $c (@circs) { + + try { + my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) ); + + my $due = $due_dt->epoch; + my $now = time; + my $fine_interval = interval_to_seconds( $c->fine_interval ); + + if ( interval_to_seconds( $c->fine_interval ) >= interval_to_seconds('1d') ) { + my $tz_offset_s = 0;; + if ($due_dt->strftime('%z') =~ /(-|\+)(\d{2}):?(\d{2})/) { + $tz_offset_s = $1 . interval_to_seconds( "${2}h ${3}m"); + } + + $due -= ($due % $fine_interval) + $tz_offset_s; + $now -= ($now % $fine_interval) + $tz_offset_s; + } + + $client->respond( + "ARG! Overdue circulation ".$c->id. + " for item ".$c->target_copy. + " (user ".$c->usr.").\n". + "\tItem was due on or before: ".localtime($due)."\n"); + + my ($fine) = $self->method_lookup('open-ils.storage.direct.money.billing.search')->run( + { xact => $c->id, voided => 'f' }, + { order_by => 'billing_ts DESC', limit => '1' } + ); + + my $last_fine; + if ($fine) { + $last_fine = $parser->parse_datetime( clense_ISO8601( $fine->billing_ts ) )->epoch; + } else { + $last_fine = $due; + $last_fine += $fine_interval * $grace; + } + + my $pending_fine_count = int( ($now - $last_fine) / $fine_interval ); + unless($pending_fine_count) { + $client->respond( "\tNo fines to create. " ); + if ($grace && $now < $due + $fine_interval * $grace) { + $client->respond( "Still inside grace period of: ". seconds_to_interval( $fine_interval * $grace)."\n" ); + } else { + $client->respond( "Last fine generated for: ".localtime($last_fine)."\n" ); + } + next; + } + + $client->respond( "\t$pending_fine_count pending fine(s)\n" ); + + for my $bill (1 .. $pending_fine_count) { + + my ($total) = $self->method_lookup('open-ils.storage.direct.money.billable_transaction_summary.retrieve')->run( $c->id ); + + if ($total && $total->balance_owed > $c->max_fine) { + $c->stop_fines('MAXFINES'); + $self->method_lookup('open-ils.storage.direct.action.circulation.update')->run( $c ); + $client->respond( + "\tMaximum fine level of ".$c->max_fine. + " reached for this circulation.\n". + "\tNo more fines will be generated.\n" ); + last; + } + + my $billing = new Fieldmapper::money::billing; + $billing->xact( $c->id ); + $billing->note( "Overdue Fine" ); + $billing->amount( $c->recuring_fine ); + + $billing->billing_ts( + DateTime->from_epoch( epoch => $last_fine + $fine_interval * $bill )->strftime('%FT%T%z') + ); + + $client->respond( + "\t\tCreating fine of ".$billing->amount." for period starting ". + localtime( + $parser->parse_datetime( + clense_ISO8601( $billing->billing_ts ) + )->epoch + )."\n" ); + + $self->method_lookup('open-ils.storage.direct.money.billing.create')->run( $billing ); + } + } catch Error with { + my $e = shift; + $client->respond( "Error processing overdue circulation [".$c->id."]:\n\n$e\n" ); + }; + } +} +__PACKAGE__->register_method( + api_name => 'open-ils.storage.action.circulation.overdue.generate_fines', + api_level => 1, + stream => 1, + method => 'generate_fines', +); + + + +my $locations; +my $statuses; +my $user_filter; +my %cache = (titles => {}, cns => {}); +sub hold_copy_targeter { + my $self = shift; + my $client = shift; + my $check_expire = shift; + my $one_hold = shift; + + $user_filter ||= $self->method_lookup('open-ils.circ.permit_hold'); + + my $time = time; + $check_expire ||= '12h'; + $check_expire = interval_to_seconds( $check_expire ); + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time - $check_expire); + $year += 1900; + $mon += 1; + my $expire_threshold = sprintf( + '%s-%0.2d-%0.2dT%s:%0.2d:%0.s2-00', + $year, $mon, $mday, $hour, $min, $sec + ); + + ($statuses) = $self->method_lookup('open-ils.storage.direct.config.copy_status.search.holdable.atomic')->run('t'); + + ($locations) = $self->method_lookup('open-ils.storage.direct.asset.copy_location.search.holdable.atomic')->run('t'); + + my $holds; + + %cache = (titles => {}, cns => {}); + + try { + if ($one_hold) { + ($holds) = $self->method_lookup('open-ils.storage.direct.action.hold_request.search.atomic') + ->run(id => $one_hold); + } else { + ($holds) = $self->method_lookup('open-ils.storage.direct.action.hold_request.search_where.atomic') + ->run( + { capture_time => undef, + prev_check_time => { '<=' => $expire_threshold }, + }, + { order_by => 'request_time,prev_check_time' } ); + push @$holds, @{ + $self->method_lookup('open-ils.storage.direct.action.hold_request.search.atomic') + ->run( + { capture_time => undef, + prev_check_time => undef }, + { order_by => 'request_time' } ) }; + } + } catch Error with { + my $e = shift; + die "Could not retrieve uncaptured hold requests:\n\n$e\n"; + }; + + $_->clear_current_copy for (@$holds); + + for my $hold (@$holds) { + my $copies; + + my @captured_copies = [ map {$_->current_copy} @$holds ]; + + if (0) { # hold isn't check-expired + # get the copies from the hold-map + # and filter on "avialable" + } else { + $copies = $self->metarecord_hold_capture($hold) if ($hold->hold_type eq 'M'); + $copies = $self->title_hold_capture($hold) if ($hold->hold_type eq 'T'); + $copies = $self->volume_hold_capture($hold) if ($hold->hold_type eq 'V'); + $copies = $self->copy_hold_capture($hold) if ($hold->hold_type eq 'C'); + } + + next unless (ref $copies); + + my @good_copies; + for my $c (@$copies) { + next if ( grep {$c->id == $_} @captured_copies); + push @good_copies, $c; + } + + my $prox_list; + $$prox_list[0] = [grep {$_->circ_lib == $hold->pickup_lib } @$copies]; + $copies = [grep {$_->circ_lib != $hold->pickup_lib } @$copies]; + + my $best = $self->choose_nearest_copy($hold, $prox_list); + + if (!$best) { + $prox_list = $self->create_prox_list( $hold->pickup_lib, $copies ); + $best = $self->choose_nearest_copy($hold, $prox_list); + } + + if ($best) { + $hold->current_copy( $best->id ); + } + + $hold->prev_check_time( 'now'); + my ($r) = $self->method_lookup('open-ils.storage.direct.action.hold_request.update')->run( $hold ); + + $client->respond("Processed hold ".$hold->id.". ". + do{ $hold->current_copy ? "Targeting copy ".$hold->current_copy." for capture." : ''; }. + "\n" + ); + } + return undef; +} +__PACKAGE__->register_method( + api_name => 'open-ils.storage.action.hold_request.copy_targeter', + api_level => 1, + stream => 1, + method => 'hold_copy_targeter', +); + + + +sub copy_hold_capture { + my $self = shift; + my $hold = shift; + my $cps = shift; + + if (!defined($cps)) { + try { + ($cps) = $self->method_lookup('open-ils.storage.direct.asset.copy.search.id.atomic') + ->run( $hold->target ); + + } catch Error with { + my $e = shift; + die "Could not retrieve initial volume list:\n\n$e\n"; + }; + } + + my @copies = grep { $_->holdable == 1 and $_->ref == 0 } @$cps; + + for (my $i = 0; $i < @copies; $i++) { + + my $cn = $cache{cns}{$copies[0]->call_number}; + my $rec = $cache{titles}{$cn->record}; + $copies[$i] = undef if ($copies[$i] && !grep{ $copies[$i]->status eq $_->id}@$statuses); + $copies[$i] = undef if ($copies[$i] && !grep{ $copies[$i]->location eq $_->id}@$locations); + $copies[$i] = undef if ( + $copies[$i] && + !($user_filter->run( $hold, $copies[$i], { title => $rec, call_number => $cn } ))[0] + ); + } + + @copies = grep { defined $_ } @copies; + + my $count = @copies; + + return unless ($count); + + my ($old_maps) = $self->method_lookup('open-ils.storage.direct.action.hold_copy_map.search.hold.atomic')->run( $hold->id ); + + $self->method_lookup('open-ils.storage.direct.action.hold_copy_map.batch.delete')->run(@$old_maps ); + + my @maps; + for my $c (@copies) { + my $m = new Fieldmapper::action::hold_copy_map; + $m->hold( $hold->id ); + $m->target_copy( $c->id ); + $m->isnew( 1 ); + push @maps, $m; + } + + $self->method_lookup('open-ils.storage.direct.action.hold_copy_map.batch.create')->run( @maps ); + + return \@copies; +} + + +sub choose_nearest_copy { + my $self = shift; + my $hold = shift; + my $prox_list = shift; + + for my $p ( 0 .. int( scalar(@$prox_list) - 1) ) { + next unless (ref $$prox_list[$p]); + my @capturable = grep { $_->status == 0 } @{ $$prox_list[$p] }; + next unless (@capturable); + return $capturable[rand(scalar(@capturable))]; + } +} + +sub create_prox_list { + my $self = shift; + my $lib = shift; + my $copies = shift; + + my @prox_list; + for my $cp (@$copies) { + my ($prox) = $self->method_lookup('open-ils.storage.asset.copy.proximity')->run( $cp->id, $lib ); + $prox_list[$prox] = [] unless defined($prox_list[$prox]); + push @{$prox_list[$prox]}, $cp; + } + return \@prox_list; +} + +sub volume_hold_capture { + my $self = shift; + my $hold = shift; + my $vols = shift; + + if (!defined($vols)) { + try { + ($vols) = $self->method_lookup('open-ils.storage.direct.asset.call_number.search.id.atomic')->run( $hold->target ); + + $cache{cns}{$_->id} = $_ for (@$vols); + + } catch Error with { + my $e = shift; + die "Could not retrieve initial volume list:\n\n$e\n"; + }; + } + + my @v_ids = map { $_->id } @$vols; + + my $cp_list; + try { + ($cp_list) = $self->method_lookup('open-ils.storage.direct.asset.copy.search.call_number.atomic')->run( \@v_ids ); + + } catch Error with { + my $e = shift; + warn "Could not retrieve copy list:\n\n$e\n"; + }; + + $self->copy_hold_capture($hold,$cp_list) if (ref $cp_list and @$cp_list); +} + +sub title_hold_capture { + my $self = shift; + my $hold = shift; + my $titles = shift; + + if (!defined($titles)) { + try { + ($titles) = $self->method_lookup('open-ils.storage.direct.biblio.record_entry.search.id.atomic')->run( $hold->target ); + + $cache{titles}{$_->id} = $_ for (@$titles); + + } catch Error with { + my $e = shift; + die "Could not retrieve initial title list:\n\n$e\n"; + }; + } + + my @t_ids = map { $_->id } @$titles; + my $cn_list; + try { + ($cn_list) = $self->method_lookup('open-ils.storage.direct.asset.call_number.search.record.atomic')->run( \@t_ids ); + + } catch Error with { + my $e = shift; + warn "Could not retrieve volume list:\n\n$e\n"; + }; + + $cache{cns}{$_->id} = $_ for (@$cn_list); + + $self->volume_hold_capture($hold,$cn_list) if (ref $cn_list and @$cn_list); +} + +sub metarecord_hold_capture { + my $self = shift; + my $hold = shift; + + my $titles; + try { + ($titles) = $self->method_lookup('open-ils.storage.ordered.metabib.metarecord.records.atomic')->run( $hold->target ); + + } catch Error with { + my $e = shift; + die "Could not retrieve initial title list:\n\n$e\n"; + }; + + try { + my @recs = map {$_->record} + @{$self->method_lookup('open-ils.storage.direct.metabib.record_descriptor.search.atomic') + ->run( record => $titles, item_type => [split '', $hold->holdable_formats] )}; + + $titles = []; + ($titles) = $self->method_lookup('open-ils.storage.direct.biblio.record_entry.search.id.atomic')->run( \@recs ); + + } catch Error with { + my $e = shift; + die "Could not retrieve format-pruned title list:\n\n$e\n"; + }; + + + $cache{titles}{$_->id} = $_ for (@$titles); + + $self->title_hold_capture($hold,$titles) if (ref $titles and @$titles); +} + 1; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/money.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/money.pm index e0ea9c56ab..e7a7e6d73a 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/money.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/money.pm @@ -1,147 +1,7 @@ package OpenILS::Application::Storage::Publisher::money; use base qw/OpenILS::Application::Storage/; -use OpenSRF::Utils qw/:datetime/; use OpenSRF::Utils::Logger qw/:level/; -use OpenSRF::EX qw/:try/; -use OpenILS::Utils::Fieldmapper; -use DateTime; -use DateTime::Format::ISO8601; my $log = 'OpenSRF::Utils::Logger'; -sub xact_summary { - my $self = shift; - my $client = shift; - my $xact = shift || ''; - - my $sql = <<" SQL"; - SELECT balance_owed - FROM money.usr_billable_summary_xact - WHERE transaction = ? - SQL - - return money::billing->db_Main->selectrow_hashref($sql, {}, "$xact"); -} -#__PACKAGE__->register_method( -# api_name => 'open-ils.storage.money.billing.billable_transaction_summary', -# api_level => 1, -# method => 'xact_summary', -#); - -my $parser = DateTime::Format::ISO8601->new; -sub generate_fines { - my $self = shift; - my $client = shift; - my $grace = shift; - my $circ = shift; - - - my @circs; - if ($circ) { - push @circs, - $self->method_lookup( - 'open-ils.storage.direct.action.circulation.search_where' - )->run( { id => $circ, stop_fines => undef } ); - } else { - push @circs, $self->method_lookup('open-ils.storage.action.circulation.overdue')->run( $grace ); - } - - for my $c (@circs) { - - try { - my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) ); - - my $due = $due_dt->epoch; - my $now = time; - my $fine_interval = interval_to_seconds( $c->fine_interval ); - - if ( interval_to_seconds( $c->fine_interval ) >= interval_to_seconds('1d') ) { - my $tz_offset_s = 0;; - if ($due_dt->strftime('%z') =~ /(-|\+)(\d{2}):?(\d{2})/) { - $tz_offset_s = $1 . interval_to_seconds( "${2}h ${3}m"); - } - - $due -= ($due % $fine_interval) + $tz_offset_s; - $now -= ($now % $fine_interval) + $tz_offset_s; - } - - $client->respond( - "ARG! Overdue circulation ".$c->id. - " for item ".$c->target_copy. - " (user ".$c->usr.").\n". - "\tItem was due on or before: ".localtime($due)."\n"); - - my ($fine) = $self->method_lookup('open-ils.storage.direct.money.billing.search')->run( - { xact => $c->id, voided => 'f' }, - { order_by => 'billing_ts DESC', limit => '1' } - ); - - my $last_fine; - if ($fine) { - $last_fine = $parser->parse_datetime( clense_ISO8601( $fine->billing_ts ) )->epoch; - } else { - $last_fine = $due; - $last_fine += $fine_interval * $grace; - } - - my $pending_fine_count = int( ($now - $last_fine) / $fine_interval ); - unless($pending_fine_count) { - $client->respond( "\tNo fines to create. " ); - if ($grace && $now < $due + $fine_interval * $grace) { - $client->respond( "Still inside grace period of: ". seconds_to_interval( $fine_interval * $grace)."\n" ); - } else { - $client->respond( "Last fine generated for: ".localtime($last_fine)."\n" ); - } - next; - } - - $client->respond( "\t$pending_fine_count pending fine(s)\n" ); - - for my $bill (1 .. $pending_fine_count) { - - my ($total) = $self->method_lookup('open-ils.storage.direct.money.billable_transaction_summary.retrieve')->run( $c->id ); - - if ($total && $total->balance_owed > $c->max_fine) { - $c->stop_fines('MAXFINES'); - $self->method_lookup('open-ils.storage.direct.action.circulation.update')->run( $c ); - $client->respond( - "\tMaximum fine level of ".$c->max_fine. - " reached for this circulation.\n". - "\tNo more fines will be generated.\n" ); - last; - } - - my $billing = new Fieldmapper::money::billing; - $billing->xact( $c->id ); - $billing->note( "Overdue Fine" ); - $billing->amount( $c->recuring_fine ); - - $billing->billing_ts( - DateTime->from_epoch( epoch => $last_fine + $fine_interval * $bill )->strftime('%FT%T%z') - ); - - $client->respond( - "\t\tCreating fine of ".$billing->amount." for period starting ". - localtime( - $parser->parse_datetime( - clense_ISO8601( $billing->billing_ts ) - )->epoch - )."\n" ); - - $self->method_lookup('open-ils.storage.direct.money.billing.create')->run( $billing ); - } - } catch Error with { - my $e = shift; - $client->respond( "Error processing overdue circulation [".$c->id."]:\n\n$e\n" ); - }; - } -} -__PACKAGE__->register_method( - api_name => 'open-ils.storage.action.circulation.overdue.generate_fines', - api_level => 1, - stream => 1, - method => 'generate_fines', -); - - 1; diff --git a/Open-ILS/src/support-scripts/hold-copy-capture.pl b/Open-ILS/src/support-scripts/hold-copy-capture.pl deleted file mode 100755 index 6449fe1d39..0000000000 --- a/Open-ILS/src/support-scripts/hold-copy-capture.pl +++ /dev/null @@ -1,354 +0,0 @@ -#!/usr/bin/perl -w -use strict; -use OpenSRF::EX qw/:try/; -use OpenSRF::System; -use OpenSRF::Application; -use OpenSRF::Utils::SettingsClient; -use OpenILS::Utils::Fieldmapper; -use OpenSRF::Utils; - -die "USAGE:\n\t$0 config_file\n" unless @ARGV; - - -# hard coded for now, option later - -my $time = time; -my $check_expire = OpenSRF::Utils::interval_to_seconds( '10m' ); - -my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time - $check_expire); -$year += 1900; -$mon += 1; -my $expire_threshold = sprintf( - '%s-%0.2d-%0.2dT%s:%0.2d:%0.s2-00', - $year, $mon, $mday, $hour, $min, $sec -); - -OpenSRF::System->bootstrap_client( config_file => $ARGV[0] ); -my $session = OpenSRF::AppSession->create('open-ils.storage'); - -my $module = OpenSRF::Utils::SettingsClient - ->new - ->config_value('apps','open-ils.circ','implementation'); - -eval "use $module; $module->initialize;"; -die "Can't load the open-ils.circ module [$module] : $@\n" if ($@); - -my $user_filter = OpenSRF::Application->method_lookup('open-ils.circ.permit_hold'); - -my $statuses = $session->request( - 'open-ils.storage.direct.config.copy_status.search.holdable.atomic', - 't')->gather(1); - -my $locations = $session->request( - 'open-ils.storage.direct.asset.copy_location.search.holdable.atomic', - 't')->gather(1); - -my $holds; - -my %cache = (titles => {}, cns => {}); - -try { - if ($ARGV[1]) { - $holds = $session->request( - 'open-ils.storage.direct.action.hold_request.search.atomic', - id => $ARGV[1] )->gather(1); - } else { - $holds = $session->request( - 'open-ils.storage.direct.action.hold_request.search_where.atomic', - { capture_time => undef, - prev_check_time => { '<=' => $expire_threshold }, - }, - { order_by => 'request_time,prev_check_time' } )->gather(1); - push @$holds, @{ - $session->request( - 'open-ils.storage.direct.action.hold_request.search.atomic', - { capture_time => undef, - prev_check_time => undef }, - { order_by => 'request_time,prev_check_time' } )->gather(1) - }; - } -} catch Error with { - my $e = shift; - die "Could not retrieve uncaptured hold requests:\n\n$e\n"; -}; - -$_->clear_current_copy for (@$holds); - -for my $hold (@$holds) { - my $copies; - - my @captured_copies = [ map {$_->current_copy} @$holds ]; - - if (0) { # hold isn't check-expired - # get the copies from the hold-map - # and filter on "avialable" - } else { - $copies = metarecord_hold_capture($hold) if ($hold->hold_type eq 'M'); - $copies = title_hold_capture($hold) if ($hold->hold_type eq 'T'); - $copies = volume_hold_capture($hold) if ($hold->hold_type eq 'V'); - $copies = copy_hold_capture($hold) if ($hold->hold_type eq 'C'); - } - - next unless (ref $copies); - - my @good_copies; - for my $c (@$copies) { - next if ( grep {$c->id == $_} @captured_copies); - push @good_copies, $c; - } - - my $prox_list; - $$prox_list[0] = [grep {$_->circ_lib == $hold->pickup_lib } @$copies]; - $copies = [grep {$_->circ_lib != $hold->pickup_lib } @$copies]; - - my $best = choose_nearest_copy($hold, $prox_list); - - if (!$best) { - $prox_list = create_prox_list( $hold->pickup_lib, $copies ); - $best = choose_nearest_copy($hold, $prox_list); - } - - if ($best) { - print "Updating hold ".$hold->id." with current_copy ".$best->id."\n"; - $hold->current_copy( $best->id ); - } - - $hold->prev_check_time( 'now'); - $session->request( - 'open-ils.storage.direct.action.hold_request.update', - $hold )->gather(1) || - warn "Could not save hold ".$hold->id."\n"; - print '-'x80 . "\n"; -} - - -sub copy_hold_capture { - my $hold = shift; - my $cps = shift; - - if (!defined($cps)) { - try { - $cps = $session->request( - 'open-ils.storage.direct.asset.copy.search.id.atomic', - $hold->target )->gather(1); - - } catch Error with { - my $e = shift; - die "Could not retrieve initial volume list:\n\n$e\n"; - }; - } - - my @copies = grep { $_->holdable == 1 and $_->ref == 0 } @$cps; - - print "Applying user defined filters for hold ".$hold->id."...\n"; - for (my $i = 0; $i < @copies; $i++) { - - my $cn = $cache{cns}{$copies[0]->call_number}; - my $rec = $cache{titles}{$cn->record}; - $copies[$i] = undef if ($copies[$i] && !grep{ $copies[$i]->status eq $_->id}@$statuses); - $copies[$i] = undef if ($copies[$i] && !grep{ $copies[$i]->location eq $_->id}@$locations); - $copies[$i] = undef if ( - $copies[$i] && - !$user_filter->run( $hold, $copies[$i], { title => $rec, call_number => $cn } ) - ); - } - - @copies = grep { defined $_ } @copies; - - my $count = @copies; - - return unless ($count); - - print "Saving $count eligible copies for hold ".$hold->id.":\n"; - - my $old_maps = $session->request( - 'open-ils.storage.direct.action.hold_copy_map.search.hold.atomic', - $hold->id )->gather(1); - - $session->request( 'open-ils.storage.direct.action.hold_copy_map.batch.delete', @$old_maps ) - ->gather(1) if (defined($old_maps) and @$old_maps); - - my @maps; - for my $c (@copies) { - my $m = new Fieldmapper::action::hold_copy_map; - $m->hold( $hold->id ); - $m->target_copy( $c->id ); - $m->isnew( 1 ); - push @maps, $m; - } - - $session->request( - 'open-ils.storage.direct.action.hold_copy_map.batch.create', - @maps )->gather(1) || - warn "Could not save copies for hold ".$hold->id."\n"; - - return \@copies; -} - - -sub choose_nearest_copy { - my $hold = shift; - my $prox_list = shift; - - for my $p ( 0 .. int( scalar(@$prox_list) - 1) ) { - next unless (ref $$prox_list[$p]); - my @capturable = grep { $_->status == 0 } @{ $$prox_list[$p] }; - next unless (@capturable); - return $capturable[rand(scalar(@capturable))]; - } -} - -sub create_prox_list { - my $lib = shift; - my $copies = shift; - - my @prox_list; - print "Creating proximity list :\n"; - for my $cp (@$copies) { - my $prox = $session->request( - 'open-ils.storage.asset.copy.proximity', - $cp->id, $lib )->gather(1); - print "\t".$cp->id." -> ".$cp->barcode." :: Proximity -> $prox\n"; - $prox_list[$prox] = [] unless defined($prox_list[$prox]); - push @{$prox_list[$prox]}, $cp; - } - print "\n"; - return \@prox_list; -} - -sub volume_hold_capture { - my $hold = shift; - my $vols = shift; - - if (!defined($vols)) { - try { - $vols = $session->request( - 'open-ils.storage.direct.asset.call_number.search.id.atomic', - $hold->target )->gather(1); - - $cache{cns}{$_->id} = $_ for (@$vols); - - } catch Error with { - my $e = shift; - die "Could not retrieve initial volume list:\n\n$e\n"; - }; - } - - my @v_ids = map { $_->id } @$vols; - - my $cp_list; - try { - $cp_list = $session->request( - 'open-ils.storage.direct.asset.copy.search.call_number.atomic', - \@v_ids )->gather(1); - - } catch Error with { - my $e = shift; - warn "Could not retrieve copy list:\n\n$e\n"; - }; - - if (ref $cp_list) { - my $count = @$cp_list; - print "Found $count possible copies for hold ".$hold->id.":\n"; - for my $cp (@$cp_list) { - print "\t".$cp->id." -> ".$cp->barcode."\n"; - } - print "\n"; - } - - copy_hold_capture($hold,$cp_list) if (ref $cp_list and @$cp_list); -} - -sub title_hold_capture { - my $hold = shift; - my $titles = shift; - - if (!defined($titles)) { - try { - $titles = $session->request( - 'open-ils.storage.direct.biblio.record_entry.search.id.atomic', - $hold->target )->gather(1); - - $cache{titles}{$_->id} = $_ for (@$titles); - - } catch Error with { - my $e = shift; - die "Could not retrieve initial title list:\n\n$e\n"; - }; - } - - my @t_ids = map { $_->id } @$titles; - my $cn_list; - try { - $cn_list = $session->request( - 'open-ils.storage.direct.asset.call_number.search.record.atomic', - \@t_ids )->gather(1); - - } catch Error with { - my $e = shift; - warn "Could not retrieve volume list:\n\n$e\n"; - }; - - if (ref $cn_list) { - my $count = @$cn_list; - print "Found $count volumes for hold ".$hold->id.":\n"; - for my $cn (@$cn_list) { - print "\t".$cn->id." -> ".$cn->label."\n"; - } - print "\n"; - } - - $cache{cns}{$_->id} = $_ for (@$cn_list); - - volume_hold_capture($hold,$cn_list) if (ref $cn_list and @$cn_list); -} - -sub metarecord_hold_capture { - my $hold = shift; - - my $titles; - try { - $titles = $session->request( - 'open-ils.storage.ordered.metabib.metarecord.records.atomic', - $hold->target - )->gather(1); - - } catch Error with { - my $e = shift; - die "Could not retrieve initial title list:\n\n$e\n"; - }; - - try { - my @recs = map {$_->record} - @{$session->request( - 'open-ils.storage.direct.metabib.record_descriptor.search.atomic', - record => $titles, - item_type => [split '', $hold->holdable_formats], - )->gather(1)}; - - $titles = []; - $titles = $session->request( - 'open-ils.storage.direct.biblio.record_entry.search.id.atomic', - \@recs )->gather(1) if (@recs); - - } catch Error with { - my $e = shift; - die "Could not retrieve format-pruned title list:\n\n$e\n"; - }; - - - if (ref $titles) { - my $count = @$titles; - print "Found $count titles for hold ".$hold->id.":\n"; - for my $title (@$titles) { - print "\t".$title->tcn_value." -> ".$title->fingerprint."\n"; - } - print "\n"; - } - - $cache{titles}{$_->id} = $_ for (@$titles); - - title_hold_capture($hold,$titles) if (ref $titles and @$titles); -} - -