ff : initial EG perl diffs import
authorBill Erickson <berick@esilibrary.com>
Fri, 25 Oct 2013 18:24:49 +0000 (14:24 -0400)
committerBill Erickson <berick@esilibrary.com>
Fri, 25 Oct 2013 18:24:49 +0000 (14:24 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Cat.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/actor.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/asset.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/biblio.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/asset.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/biblio.pm

index 9c9efc4..1a8ff3c 100644 (file)
@@ -72,6 +72,286 @@ my $set_ou_settings;
 #}
 
 __PACKAGE__->register_method(
+    method => 'fetch_web_action_print_template',
+    api_name    => 'open-ils.actor.web_action_print_template.fetch',
+    signature => q/
+        Given a blob of ILL action status information, generate some useful HTML for printing, based on the acting org unit and action.
+        @param authtoken Login session key
+        @pararm blob Action status blob
+    /
+);
+sub fetch_web_action_print_template {
+    my( $self, $client, $owner, $focus, $action, $direction ) = @_;
+    my $e = new_editor();
+    my $orgs = $U->get_org_ancestors($owner);
+
+    return $e->search_actor_web_action_print_template([
+        { owner     => $orgs,
+          focus     => $focus,
+          -or => [{action => $action},{action => undef}],
+          -or => [{direction => $direction},{direction => undef}]
+        },
+        { order_by => [
+            { class     => 'awapt',
+              field     => 'action',
+              compare   => { '=' => undef }
+            },
+            { class     => 'awapt',
+              field     => 'direction',
+              compare   => { '=' => undef }
+            }
+        ]}
+    ])->[0];
+}
+
+sub find_user_lib {
+    my $lib = shift;
+    my $e = new_editor();
+
+    $lib = $e->retrieve_actor_org_unit($lib) if (!ref($lib));
+
+    while ($lib) {
+        my $type = $e->retrieve_actor_org_unit_type($lib->ou_type);
+        return $lib if ($U->is_true($type->can_have_users));
+        return undef unless ($lib->parent_ou);
+        $lib = $e->retrieve_actor_org_unit($lib->parent_ou);
+    }
+
+    return undef;
+}
+
+__PACKAGE__->register_method(
+       method  => "remote_auth_init",
+       api_name        => "open-ils.actor.remote.authenticate.init",
+);
+sub remote_auth_init {
+    my($self, $client, $uname, $uhome) = @_;
+
+    $uhome = find_user_lib($uhome);
+    return 0 if (!$uhome);
+
+    $uhome = $uhome->id;
+
+    my $e = new_editor();
+    my $user = $e->search_actor_user({
+        usrname => "$uname:$uhome", 
+        home_ou => $uhome,
+        # deleted accounts must be re-fetched, updated, and
+        # un-deleted in open-ils.actor.remote.authenticate.complete
+        deleted => 'f'
+    });
+
+    return -1 unless @$user; # No local user matching the requested criteria, yet
+    return $U->simplereq( "open-ils.auth", "open-ils.auth.authenticate.init", "$uname:$uhome" );
+}
+
+__PACKAGE__->register_method(
+       method  => "remote_auth_complete",
+       api_name        => "open-ils.actor.remote.authenticate.complete",
+);
+sub remote_auth_complete {
+    my($self, $client, $args) = @_;
+
+    my $uhome = find_user_lib($$args{home});
+    return $U->DB_UPDATE_FAILED() if (!$uhome);
+
+    $$args{home} = $uhome->id;
+
+    my $actor = OpenSRF::AppSession->create("open-ils.actor");
+    my $ugroup = $actor->request(
+        "open-ils.actor.ou_setting.ancestor_default",
+        $$args{home},"ff.remote.user_cache.default_group"
+    )->gather(1);
+
+    if ($ugroup && ref($ugroup)) {
+        $ugroup = $ugroup->{'value'};
+    } else {
+        $ugroup = 1;
+    }
+
+    my $e = new_editor();
+    my $user = $e->search_actor_user({
+        usrname => "$$args{username}:$$args{home}", 
+        home_ou => $$args{home}
+    })->[0];
+
+    my $remote_user;
+    if (!$user or $U->is_true($user->deleted)) {
+
+        $remote_user = $U->simplereq( 
+            "fulfillment.laicore", 
+            "fulfillment.laicore.lookup_user", 
+            $$args{home}, $$args{username}, $$args{password} 
+        );
+
+        $remote_user = OpenSRF::Utils::JSON->JSON2perl($remote_user) 
+            if (!ref($remote_user)); # XXX arg.... (what's this now?)
+    }
+
+
+    my $seed_needed = 0;
+    if ($user) {
+        if ($U->is_true($user->deleted)) {
+            my $evt = resurrect_user($user, $remote_user, $ugroup, $args);
+            return $evt if $evt;
+            $seed_needed = 1;
+        }
+    } else {
+
+        my $u = $remote_user;
+
+        if ($u && !$$u{error}) { # create user ...
+            $seed_needed = 1;
+
+            $logger->info("FF creating new user for $$args{username}");
+
+            for my $k (keys %$u) {
+                delete $$u{$k} if ref($$u{$k});
+            }
+
+            my $evt;
+            my $patron = Fieldmapper::actor::user->new();
+            my $card = Fieldmapper::actor::card->new();
+
+            # how we find them locally
+            $patron->usrname( "$$args{username}:$$args{home}" );
+            $patron->passwd( $$args{password} );
+            $patron->profile( $ugroup );
+            $patron->home_ou( $$args{home} );
+            $patron->ident_type(3);
+
+            # what we can glean from the remote system
+            $patron->first_given_name( $$u{given_name} || '...' );
+            $patron->family_name( $$u{surname} || '...' );
+            $patron->suffix( $$u{suffix} );
+            $patron->prefix( $$u{prefix} );
+            $patron->alias( $$u{initials} );
+        
+            # Set up all of the virtual IDs, isnew, etc.
+            $patron->isnew(1);
+            $patron->id(-1);
+            $patron->card(-1);
+        
+            $card->isnew(1);
+            $card->id(-1);
+            $card->usr(-1);
+            $card->org($$args{home});
+            $card->barcode($$u{user_barcode} || $$u{user_id} || $$args{username});
+
+            $patron->cards([$card]);
+
+            my $session = $apputils->start_db_session();
+            $logger->info("Creating new remote-proxy patron...");
+
+            # $new_patron is the patron in progress.  $patron is the original patron
+            # passed in with the method.  new_patron will change as the components
+            # of patron are added/updated.
+
+            my $new_patron;
+
+            # unflesh the real items on the patron
+            $patron->card( $patron->card->id ) if(ref($patron->card));
+
+            # create the patron first so we can use his id
+               my $id = $session->request( "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
+            return $U->DB_UPDATE_FAILED($patron) unless $id;
+
+            $logger->info("Successfully created new user [$id] in DB");
+
+            $new_patron = $session->request( "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1);
+
+            ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron);
+            return $evt if $evt;
+
+            # re-update the patron
+            ( $new_patron, $evt ) = _update_patron($session, $new_patron, undef, 1);
+            return $evt if $evt;
+
+            $apputils->commit_db_session($session);
+        }
+    }
+
+    $$args{username} = "$$args{username}:$$args{home}";
+
+    if ($seed_needed) {
+        # initial call to .init exited early w/ no seed,
+        # so we need to create one now that we have a user
+        my $seed = $U->simplereq( 
+            "open-ils.auth", 
+            "open-ils.auth.authenticate.init", 
+            $$args{username}
+        );
+
+        $$args{password} = md5_hex($seed . md5_hex($$args{password}));
+    }
+
+    return $U->simplereq( "open-ils.auth", "open-ils.auth.authenticate.complete", $args );
+}
+
+# the steps involved in user resurrection are just different 
+# enough from user creation that blending them into one chain
+# of code gets messy.  There may some room for combining
+# and refactoring here, since there is some duplication.
+sub resurrect_user {
+    my $user = shift;
+    my $remote_user = shift;
+    my $ugroup = shift;
+    my $args = shift;
+
+    $logger->info("Resurrecting deleted user ".$user->id);
+
+    my $e = new_editor(xact => 1);
+
+    my $card = $e->search_actor_card({usr => $user->id})->[0];
+
+    if (!$card) {
+        $card = Fieldmapper::actor::card->new();
+        $card->usr($user->id);
+        $card->isnew(1); # for our own tracking
+        $user->clear_card;
+    }
+
+    $user->deleted('f');
+    $user->usrname("$$args{username}:$$args{home}");
+    $user->passwd($$args{password});
+    $user->profile($ugroup);
+    $user->home_ou($$args{home});
+    $user->ident_type(3);
+
+    # what we can glean from the remote system
+    my $u = $remote_user;
+    $user->first_given_name($$u{given_name} || '...');
+    $user->family_name($$u{surname} || '...');
+    $user->suffix($$u{suffix});
+    $user->prefix($$u{prefix});
+    $user->alias($$u{initials});
+    $card->org($$args{home});
+    $card->barcode($$u{user_barcode} || $$u{user_id} || $$args{username});
+
+    $e->update_actor_user($user) or return $e->die_event;
+
+    if ($card->isnew) {
+        # create the new card and update the user
+        # object to refer to the card
+
+        $e->create_actor_card($card) or return $e->die_event;
+
+        # created a new card, tell the user object about it.
+        $user->card($card->id);
+        $e->update_actor_user($user) or return $e->die_event;
+
+    } else {
+        $e->update_actor_card($card) or return $e->die_event;
+    }
+
+    $e->commit;
+    return;
+}
+
+
+
+
+__PACKAGE__->register_method(
     method  => "update_user_setting",
     api_name    => "open-ils.actor.patron.settings.update",
 );
index fbf140a..322c83f 100644 (file)
@@ -125,6 +125,24 @@ sub create_record_xml {
     return $s;
 }
 
+__PACKAGE__->register_method(
+    method      => "purge_bibs_by_owner",
+    api_name    => "open-ils.cat.biblio.record.purge_by_owner",
+    signature   => q/ Inserts a new biblio with the given XML /
+);
+
+sub purge_bibs_by_owner {
+       my( $self, $client, $auth, $owner ) = @_;
+
+    my $e = new_editor(authtoken => $auth);
+    return $e->die_event unless $e->checkauth;
+    return $e->die_event unless $e->allowed('UPDATE_MARC', $owner);
+
+    return OpenSRF::AppSession
+        ->create("open-ils.storage")
+        ->request("open-ils.storage.biblio.record_entry.purge_by_owner", $owner )
+        ->gather(1);
+}
 
 
 __PACKAGE__->register_method(
index 76f1cb0..57947b6 100644 (file)
@@ -1869,4 +1869,214 @@ sub get_copy_due_date {
 # {"select":{"acp":["id"],"circ":[{"aggregate":true,"transform":"count","alias":"count","column":"id"}]},"from":{"acp":{"circ":{"field":"target_copy","fkey":"id","type":"left"},"acn"{"field":"id","fkey":"call_number"}}},"where":{"+acn":{"record":200057}}
 
 
+__PACKAGE__->register_method(
+       method  => "item_transaction_disposition",
+       api_name        => "open-ils.circ.item.transaction.disposition",
+       signature => {
+        desc => q/Given an item barcode, determines the disposition of the
+            copy with regard to open transactions.  The goal is to report 
+            on where the copy is going (if en route to or from a hold or 
+            transit) or whether the copy is circulating.  Since it's 
+            possible for more than one copy with the provided barcode may
+            have some relationship to the context org unit, some data is
+            returned as arrays, while other data is returned as a single
+            object.  
+
+            Lender transactions each are limited to one occurrence in total, 
+            because they refer to a specific copy, which lives within ctx_ou.  
+            All other actions may refer to multiple copies and thus have 
+            multiple transactions.
+
+            Lender transit is the transit back home of the loaned copy
+            Borrower transits are all transits to elsewhere by copies
+            owned elsewhere.
+            /,
+        params => [
+            {desc => 'Authentication token', type => 'string'},
+            {desc => 'Context Org Unit ID', type => 'number'},
+            {desc => 'Copy Barcodde', type => 'string'},
+        ],
+        return => {desc => q/
+            Array of copies, sorted by locally owned copy first, followed
+            by copies owned by others. Each field in the response, minus
+            the 'copy' field, is optionally.
+            {   copy    : <copy>
+                circ    : <circ>
+                hold    : <hold>
+                transit : <transit>
+            }
+        /}
+    }
+);
+
+sub item_transaction_disposition {
+    my ($self, $client, $auth, $ctx_ou, $barcode) = @_;
+
+    my $e = new_editor(authtoken => $auth);
+    return $e->event unless $e->checkauth;
+    return $e->event unless $e->allowed('VIEW_CIRCULATIONS'); # catchall
+
+    # context ou may be a parent or a child of the various transaction
+    # and item owning lib org units.
+    my $org_list = $U->get_org_full_path($ctx_ou);
+
+    my $local_copy = $e->search_asset_copy({
+        deleted  => 'f',
+        barcode  => $barcode,
+        circ_lib => $org_list
+    })->[0];
+    
+    my $remote_copies = $e->search_asset_copy({
+        deleted  => 'f',
+        barcode  => $barcode,
+        circ_lib => {'not in' => $org_list}
+    });
+
+    my @resp;
+
+    push(@resp, 
+        collect_copy_transactions(
+            $e, $local_copy->id, $ctx_ou, $org_list, 1)
+    ) if $local_copy;
+
+    push(@resp, 
+        collect_copy_transactions(
+            $e, $_->id, $ctx_ou, $org_list)
+    ) for @$remote_copies;
+
+    return \@resp;
+}
+
+sub collect_copy_transactions {
+    my ($e, $copy_id, $ctx_ou, $org_list, $local) = @_;
+    my $next_action = '';
+
+    my $transit_flesh = {
+        flesh => 4,
+        flesh_fields => {
+            atc  => ['source', 'dest', 'hold_transit_copy'],
+            ahtc => ['hold'],
+            ahr  => ['usr'],
+            au   => ['card'],
+            aou  => ['ill_address']
+        }
+    };
+
+    my $circ_flesh = {
+        flesh => 2,
+        flesh_fields => {
+            circ => ['usr'],
+            au   => ['card']
+        }
+    };
+
+    my $hold_flesh = {
+        flesh => 3, 
+        flesh_fields => {
+            ahr  => ['transit', 'usr', 'cancel_cause'],
+            au   => ['card'],
+            ahtc => ['source', 'dest']
+        }
+    };
+
+    my %resp = (
+        copy => $e->retrieve_asset_copy([
+            $copy_id, {
+                flesh => 3,
+                flesh_fields => { 
+                    acp => ['call_number', 'status'],
+                    acn => ['record'],
+                    # flesh simple_record for now as a hack to display title/author
+                    bre => ['simple_record']
+                }
+            }
+        ])
+    );
+
+    $resp{copy}->call_number->record->clear_marc;
+
+    my $hold = $e->search_action_hold_request([
+        {   fulfillment_time => undef,
+            cancel_time => undef,
+            frozen => 'f',
+            current_copy => $copy_id
+        }, $hold_flesh 
+    ])->[0];
+
+    if ($hold) {
+        if ($local) {
+            $next_action = 'ill-home-capture' unless $hold->capture_time;
+            $resp{can_retarget_hold} = 1;
+
+        } else {
+            # for non-local copies, only the borrower can cancel 
+            # or retarget the hold
+            if (grep {$hold->request_lib == $_} @$org_list) {
+                $resp{can_cancel_hold} = 1;
+                $resp{can_retarget_hold} = 1;
+            }
+
+            if ($hold->pickup_lib == $ctx_ou) {
+
+                # hold is en route to here, maybe there is work for us to do
+    
+                if ($hold->shelf_time) {
+                    $next_action = 'ill-foreign-checkout';
+                } elsif ($hold->capture_time) {
+                    $next_action = 'ill-foreign-receive';
+                }
+            }
+        }
+
+        $resp{hold} = $hold;
+    }
+
+    # non-local copies must be circulting "here" to be relevant
+    my %circ_filter = (circ_lib => $org_list) unless $local;
+
+    my $circ = $e->search_action_circulation([{
+        checkin_time => undef,
+        target_copy => $copy_id,
+        %circ_filter
+    }, $circ_flesh])->[0];
+
+    if ($circ) {
+        $next_action = 'ill-foreign-checkin' unless $local;
+        $resp{circ} = $circ;
+    }
+
+    my %transit_filter;
+
+    # non-local copies must be coming to / going from "here" to be relevant
+    $transit_filter{'-or'} = 
+        [{dest => $org_list}, {source => $org_list}]
+        unless $local;
+
+    # no need to re-fetch transits linked to holds
+    $transit_filter{id} = {'<>' => $hold->transit->id}
+        if $hold and $hold->transit;
+
+    my $transit = $e->search_action_transit_copy([{
+        target_copy => $copy_id,
+        dest_recv_time => undef,
+        %transit_filter
+    }, $transit_flesh])->[0];
+
+    if ($transit) {
+        if ($transit->dest->id == $ctx_ou) {
+            if ($local) {
+                $next_action = 'transit-home-receive' 
+            } else {
+                $next_action = 'transit-foreign-return';
+            }
+        }
+        $resp{transit} = $transit;
+    }
+
+    $resp{next_action} = $next_action;
+    return \%resp;
+}
+
+
+
 1;
index b390688..db07928 100644 (file)
@@ -330,6 +330,7 @@ sub run_method {
         $circulator->do_renew();
     }
 
+    my $remote = 0;
     if( $circulator->bail_out ) {
 
         my @ee;
@@ -347,6 +348,7 @@ sub run_method {
     } else {
 
         $circulator->editor->commit;
+        $remote = 1;
 
         if ($circulator->generate_lost_overdue) {
             # Generating additional overdue billings has to happen after the 
@@ -361,6 +363,78 @@ sub run_method {
 
     $circulator->script_runner->cleanup if $circulator->script_runner;
 
+    if ($remote) {
+        my $ff_action = $circulator->ff_action || '';
+        my $cl = $circulator->circ_lib;
+        $cl = $cl->id if (ref $cl);
+    
+        # user_barcode is not fleshed (or needed) in all contexts
+        my $user_barcode = $circulator->patron->card->barcode
+            if $circulator->patron and $circulator->patron->card;
+
+        $logger->info("circulator: performing FF action '$ff_action'");
+
+        my $FF = OpenSRF::AppSession->create('fulfillment.laicore');
+
+        if ($ff_action eq 'ill-home-capture') {
+            $FF->request( 'fulfillment.laicore.circ.lender.checkout', 
+                $cl, $circulator->copy->barcode )->gather(1);
+
+        } elsif ($ff_action eq 'ill-foreign-receive') {
+
+            # create the temporary copy for the borrower
+            my $tmp_copy = $FF->request( 
+                'fulfillment.laicore.item.create_for_borrower',
+                $cl, $circulator->copy->id)->gather(1);
+
+            if ($tmp_copy) {
+                my $bc = $tmp_copy->{barcode};
+
+                $logger->info("circulator: created tmp copy for ".
+                    "borrower, attempting hold placement for $bc at $cl");
+            
+                # place a hold on the temp copy for the borrower
+                # at the borrowing library
+
+                # ensure the borrower hold is placed against the same
+                # patron for which the FF hold was captured and transited.
+                $user_barcode = $circulator->editor->retrieve_actor_user([
+                    $circulator->hold->usr, 
+                    {flesh => 1, flesh_fields => {au => ['card']}}
+                ])->card->barcode;
+
+                $FF->request( 
+                    'fulfillment.laicore.hold.borrower.place', 
+                    $cl, $bc, $user_barcode)->gather(1);
+
+                # check the temp copy in at the borrowing library
+                # to capture the hold for the patron.
+
+                $FF->request( 
+                    'fulfillment.laicore.circ.borrower.checkin', 
+                    $cl, $bc, $user_barcode)->gather(1);
+
+            } else {
+
+                # some connectors may not allow us to create borrower copies.
+                $logger->info("FF no borrower copy created for copy ". 
+                    $circulator->copy->barcode.", skipping borrower library hold");
+            }
+
+        } elsif ($ff_action eq 'ill-foreign-checkout') {
+            $FF->request( 'fulfillment.laicore.circ.borrower.checkout', 
+                $cl, $circulator->copy->barcode, $user_barcode)->gather(1);
+
+        } elsif ($ff_action eq 'ill-foreign-checkin') {
+            $FF->request( 'fulfillment.laicore.circ.borrower.checkin', 
+                $cl, $circulator->copy->barcode )->gather(1);
+
+        } elsif ($ff_action eq 'transit-home-receive') {
+            $FF->request( 'fulfillment.laicore.circ.lender.checkin', 
+                $cl, $circulator->copy->barcode )->gather(1);
+        }
+    }
+
     return undef if $circulator->bail_out;
 
     $circulator->do_hold_notify($circulator->notify_hold)
@@ -535,6 +609,7 @@ my @AUTOLOAD_FIELDS = qw/
     skip_permit_key
     skip_deposit_fee
     skip_rental_fee
+    ff_action
     use_booking
     generate_lost_overdue
     clear_expired
index 9de0fca..36a347e 100644 (file)
@@ -781,8 +781,10 @@ sub cancel_hold {
         return 1;
     }
 
+    my $copy = $e->retrieve_asset_copy($hold->current_copy) if $hold->current_copy;
+
     # If the hold is captured, reset the copy status
-    if( $hold->capture_time and $hold->current_copy ) {
+    if( $hold->capture_time and $copy ) {
 
         my $copy = $e->retrieve_asset_copy($hold->current_copy)
             or return $e->die_event;
@@ -834,6 +836,26 @@ sub cancel_hold {
         $U->create_events_for_hook('hold_request.cancel.staff', $hold, $hold->pickup_lib);
     }
 
+    # perform FF tasks after the hold is successfully canceled -------
+    # do not block on FF calls.  Fire and forget.
+
+    # the hold within the lender ILS is fulfilled the moment the FF hold is 
+    # captured.  It makes no sense to cancel the lender-ILS hold after that.
+    if ($copy and !$hold->capture_time) {
+        my $ses = OpenSRF::AppSession->create('fulfillment.laicore');
+        $ses->request(
+            'fulfillment.laicore.hold.lender.delete_earliest',
+            $copy->source_lib,
+            $copy->barcode
+        );
+    }
+
+    if ($hold->shelf_time) {
+        # The requested copy has made it to the borrower and a tmp copy
+        # has been created w/ a hold.
+        # TODO: cancel the hold within the borrower ILS on the tmp copy
+    }
+
     return 1;
 }
 
@@ -1919,7 +1941,9 @@ sub _reset_hold {
 
     my $hid = $hold->id;
 
-    if( $hold->capture_time and $hold->current_copy ) {
+    my $copy = $e->retrieve_asset_copy($hold->current_copy) if $hold->current_copy;
+
+    if( $hold->capture_time and $copy ) {
 
         my $copy = $e->retrieve_asset_copy($hold->current_copy)
             or return $e->die_event;
@@ -1962,6 +1986,16 @@ sub _reset_hold {
     $e->update_action_hold_request($hold) or return $e->die_event;
     $e->commit;
 
+    # TODO: we should only cancel the lender hold 
+    # if the FF hold has not yet been captured ?
+    if ($copy) {
+        $U->simplereq(
+            "fulfillment.laicore",
+            "fulfillment.laicore.hold.lender.delete_earliest",
+            $copy->source_lib, $copy->barcode
+        );
+    }
+
     $U->storagereq(
         'open-ils.storage.action.hold_request.copy_targeter', undef, $hold->id );
 
@@ -2499,11 +2533,11 @@ sub create_ranged_org_filter {
     return () if $depth == $top_org->ou_type->depth;
 
     my $org_list = $U->storagereq('open-ils.storage.actor.org_unit.descendants.atomic', $selection_ou, $depth);
-    %org_filter = (circ_lib => []);
-    push(@{$org_filter{circ_lib}}, $_->id) for @$org_list;
+    %org_filter = (circ_lib => {'not in' => []});
+    push(@{$org_filter{circ_lib}{'not in'}}, $_->id) for @$org_list;
 
     $logger->info("hold org filter at depth $depth and selection_ou ".
-        "$selection_ou created list of @{$org_filter{circ_lib}}");
+        "$selection_ou created list of @{$org_filter{circ_lib}{'not in'}}");
 
     return %org_filter;
 }
index 4572dcc..e545889 100644 (file)
@@ -150,7 +150,7 @@ use base qw/actor/;
 
 __PACKAGE__->table( 'actor_card' );
 __PACKAGE__->columns( Primary => qw/id/ );
-__PACKAGE__->columns( Essential => qw/usr barcode active/ );
+__PACKAGE__->columns( Essential => qw/usr barcode active org/ );
 
 #-------------------------------------------------------------------------------
 package actor::user_access_entry;
index 30109db..6c63ca9 100644 (file)
@@ -72,7 +72,7 @@ __PACKAGE__->columns( Essential => qw/call_number barcode creator create_date ed
                    fine_level circulate deposit price ref opac_visible
                    circ_as_type circ_modifier deposit_amount location mint_condition
                    holdable dummy_title dummy_author deleted alert_message
-                   age_protect floating cost status_changed_time active_date/ );
+                   age_protect floating cost status_changed_time active_date source_lib/ );
 
 #-------------------------------------------------------------------------------
 package asset::copy_part_map;
index 732dd9a..51f546a 100644 (file)
@@ -11,7 +11,7 @@ use base qw/biblio/;
 biblio::record_entry->table( 'biblio_record_entry' );
 biblio::record_entry->columns( Essential => qw/id tcn_source tcn_value creator editor
                       create_date edit_date source active quality owner share_depth
-                      deleted marc last_xact_id fingerprint/ );
+                      deleted marc last_xact_id fingerprint remote_id/ );
 
 #-------------------------------------------------------------------------------
 package biblio::record_note;
index 9692f27..d09a9aa 100644 (file)
@@ -15,6 +15,8 @@ use OpenILS::Utils::Penalty;
 use POSIX qw(ceil);
 use OpenILS::Application::Circ::CircCommon;
 use OpenILS::Application::AppUtils;
+use FulfILLment::AT::Reactor::ItemLoad;
+use FulfILLment::AT::Reactor::ItemRefresh;
 my $U = "OpenILS::Application::AppUtils";
 
 # Used in build_hold_sort_clause().  See the hash %order_by_sprintf_args in
@@ -1308,7 +1310,7 @@ sub new_hold_copy_targeter {
                               frozen => 'f',
                               prev_check_time => { '<=' => $expire_threshold },
                             },
-                            { order_by => 'selection_depth DESC, request_time,prev_check_time' } ) ];
+                            { order_by => 'selection_depth, request_time,prev_check_time' } ) ];
 
             # find all the holds holds needing first time targeting
             push @$holds, action::hold_request->search(
@@ -1317,7 +1319,7 @@ sub new_hold_copy_targeter {
                             prev_check_time => undef,
                             frozen => 'f',
                             cancel_time => undef,
-                            { order_by => 'selection_depth DESC, request_time' } );
+                            { order_by => 'selection_depth, request_time' } );
         } else {
 
             # find all the holds holds needing first time targeting ONLY
@@ -1327,7 +1329,7 @@ sub new_hold_copy_targeter {
                             prev_check_time => undef,
                             cancel_time => undef,
                             frozen => 'f',
-                            { order_by => 'selection_depth DESC, request_time' } ) ];
+                            { order_by => 'selection_depth, request_time' } ) ];
         }
     } catch Error with {
         my $e = shift;
@@ -1374,6 +1376,8 @@ sub new_hold_copy_targeter {
 
     for my $hold (@$holds) {
         try {
+                       my $old_current_copy = $hold->current_copy;
+
             #start a transaction if needed
             if ($self->method_lookup('open-ils.storage.transaction.current')->run) {
                 $log->debug("Cleaning up after previous transaction\n");
@@ -1382,6 +1386,7 @@ sub new_hold_copy_targeter {
             $self->method_lookup('open-ils.storage.transaction.begin')->run( $client );
             $log->info("Processing hold ".$hold->id."...\n");
 
+
             #first, re-fetch the hold, to make sure it's not captured already
             $hold->remove_from_object_index();
             $hold = action::hold_request->retrieve( $hold->id );
@@ -1439,6 +1444,10 @@ sub new_hold_copy_targeter {
                                     ( $lang    ? (item_lang => $lang)                : () ),
                                 )
                     ) {
+
+                        FulfILLment::AT::Reactor::ItemLoad->ByBib( { target => $_->to_fieldmapper } )
+                            for ( metabib::metarecord->retrieve($hold->target)->source_records );
+
                         my ($rtree) = $self
                             ->method_lookup( 'open-ils.storage.biblio.record_entry.ranged_tree')
                             ->run( $r->id, $hold->selection_ou, $hold->selection_depth );
@@ -1453,6 +1462,11 @@ sub new_hold_copy_targeter {
                     }
                 }
             } elsif ($hold->hold_type eq 'T') {
+
+                FulfILLment::AT::Reactor::ItemLoad->ByBib(
+                    { target => biblio::record_entry->retrieve($hold->target)->to_fieldmapper }
+                );
+
                 my ($rtree) = $self
                     ->method_lookup( 'open-ils.storage.biblio.record_entry.ranged_tree')
                     ->run( $hold->target, $hold->selection_ou, $hold->selection_depth );
@@ -1470,6 +1484,11 @@ sub new_hold_copy_targeter {
                         ) if ($cn && @{ $cn->copies });
                 }
             } elsif ($hold->hold_type eq 'V') {
+
+                FulfILLment::AT::Reactor::ItemLoad->ByBib(
+                    { target => asset::call_number->retrieve($hold->target)->record->to_fieldmapper }
+                );
+
                 my ($vtree) = $self
                     ->method_lookup( 'open-ils.storage.asset.call_number.ranged_tree')
                     ->run( $hold->target, $hold->selection_ou, $hold->selection_depth );
@@ -1501,6 +1520,11 @@ sub new_hold_copy_targeter {
                     ) if ($itree && @{ $itree->items });
                     
             } elsif  ($hold->hold_type eq 'C' || $hold->hold_type eq 'R' || $hold->hold_type eq 'F') {
+
+                FulfILLment::AT::Reactor::ItemRefresh->ByItem(
+                    { target => asset::copy->retrieve($hold->target)->to_fieldmapper }
+                );
+
                 my $_cp = asset::copy->retrieve($hold->target);
                 push @$all_copies, $_cp if $_cp;
             }
@@ -1631,7 +1655,11 @@ sub new_hold_copy_targeter {
                 $hold
             );
 
-            $all_copies = [ grep { ''.$_->circ_lib ne $pu_lib && ( $_->status == 0 || $_->status == 7 ) } @good_copies ];
+            #$all_copies = [ grep { ''.$_->circ_lib ne $pu_lib && ( $_->status == 0 || $_->status == 7 ) } @good_copies ];
+            $all_copies = [];
+            for my $prox (keys %$prox_list) {
+                push @$all_copies, @{$$prox_list{$prox}};
+            }
 
             my $min_prox = [ sort keys %$prox_list ]->[0];
             my $best;
@@ -1783,6 +1811,31 @@ sub new_hold_copy_targeter {
             }
 
             $self->method_lookup('open-ils.storage.transaction.commit')->run;
+
+            # FF-----------------
+            my $skip_target = 0;
+                       if ($old_current_copy && $best && ($best->id == $old_current_copy->id)) { # old copy and new copy are the same, leave it alone
+                $skip_target = 1;
+                       } elsif ($old_current_copy) { # old copy to remotely untarget, since we have a new one
+                               $U->simplereq(
+                                       "fulfillment.laicore",
+                    "fulfillment.laicore.hold.lender.delete_earliest",
+                                       '' . $old_current_copy->source_lib,
+                                       $old_current_copy->barcode
+                               );
+                       }
+
+                       if ($best && !$skip_target) { # new copy to target
+                               $U->simplereq(
+                                       "fulfillment.laicore",
+                    "fulfillment.laicore.hold.lender.place",
+                                       ''. $best->source_lib,
+                                       $best->barcode
+                               );
+                       }
+            # FF-----------------
+
+
             $log->info("\tProcessing of hold ".$hold->id." complete.");
 
             push @successes,
index 4dc3e47..df9df5d 100644 (file)
@@ -789,7 +789,7 @@ sub cn_ranged_tree {
         actor::org_unit
             ->db_Main
             ->selectcol_arrayref(
-                'SELECT id FROM actor.org_unit_descendants(?,?)',
+                'SELECT id FROM actor.org_unit WHERE id NOT IN (SELECT id  FROM actor.org_unit_descendants(?,?))',
                 {},
                 $ou,
                 $depth
@@ -839,7 +839,7 @@ sub issuance_ranged_tree {
         actor::org_unit
             ->db_Main
             ->selectcol_arrayref(
-                'SELECT id FROM actor.org_unit_descendants(?,?)',
+                'SELECT id FROM actor.org_unit WHERE id NOT IN (SELECT id  FROM actor.org_unit_descendants(?,?))',
                 {},
                 $ou,
                 $depth
index 8d8648d..a211fdc 100644 (file)
@@ -120,8 +120,8 @@ sub record_ranged_tree {
     my $offset = shift || 0;
 
     my $ou_sql = defined($depth) ?
-            "SELECT id FROM actor.org_unit_descendants(?,?)":
-            "SELECT id FROM actor.org_unit_descendants(?)";
+        "SELECT id FROM actor.org_unit WHERE id NOT IN (SELECT id  FROM actor.org_unit_descendants(?,?))":
+        "SELECT id FROM actor.org_unit WHERE id NOT IN (SELECT id FROM actor.org_unit_descendants(?))";
 
     my $ou_list =
         actor::org_unit
@@ -209,6 +209,21 @@ __PACKAGE__->register_method(
     cachable    => 1,
 );
 
+sub purge_bibs_by_owner {
+       my $self = shift;
+       my $client = shift;
+       my $owner = ''.shift;
+
+       return biblio::record_entry->db_Main->do( 'DELETE FROM biblio.record_entry WHERE owner = ?', {}, $owner );
+}
+__PACKAGE__->register_method(
+       api_name        => 'open-ils.storage.biblio.record_entry.purge_by_owner',
+       method          => 'purge_bibs_by_owner',
+       api_level       => 1,
+       argc            => 1,
+       cachable        => 1,
+);
+
 sub record_by_copy {
     my $self = shift;
     my $client = shift;