LP 1779467: Fix Error When Marking Item on Hold as Discard/Weed
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Application / Circ.pm
index cd3294e..0dfebd3 100644 (file)
@@ -8,6 +8,7 @@ use OpenILS::Application::Circ::Survey;
 use OpenILS::Application::Circ::StatCat;
 use OpenILS::Application::Circ::Holds;
 use OpenILS::Application::Circ::HoldNotify;
+use OpenILS::Application::Circ::CircNotify;
 use OpenILS::Application::Circ::CreditCard;
 use OpenILS::Application::Circ::Money;
 use OpenILS::Application::Circ::NonCat;
@@ -19,14 +20,13 @@ use DateTime::Format::ISO8601;
 
 use OpenILS::Application::AppUtils;
 
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::AppSession;
 use OpenILS::Utils::ModsParser;
 use OpenILS::Event;
 use OpenSRF::EX qw(:try);
 use OpenSRF::Utils::Logger qw(:logger);
 use OpenILS::Utils::Fieldmapper;
-use OpenILS::Utils::Editor;
 use OpenILS::Utils::CStoreEditor q/:funcs/;
 use OpenILS::Const qw/:const/;
 use OpenSRF::Utils::SettingsClient;
@@ -42,36 +42,42 @@ my $holdcode    = "OpenILS::Application::Circ::Holds";
 # ------------------------------------------------------------------------
 
 sub initialize {
-       my $self = shift;
-       OpenILS::Application::Circ::Circulate->initialize();
+    my $self = shift;
+    OpenILS::Application::Circ::Circulate->initialize();
 }
 
 
 __PACKAGE__->register_method(
-       method => 'retrieve_circ',
-       authoritative   => 1,
-       api_name        => 'open-ils.circ.retrieve',
-       signature => q/
-               Retrieve a circ object by id
-               @param authtoken Login session key
-               @pararm circid The id of the circ object
-       /
+    method => 'retrieve_circ',
+    authoritative   => 1,
+    api_name    => 'open-ils.circ.retrieve',
+    signature => q/
+        Retrieve a circ object by id
+        @param authtoken Login session key
+        @pararm circid The id of the circ object
+        @param all_circ Returns an action.all_circulation_slim object instead
+            of an action.circulation object to pick up aged circs.
+    /
 );
+
 sub retrieve_circ {
-       my( $s, $c, $a, $i ) = @_;
-       my $e = new_editor(authtoken => $a);
-       return $e->event unless $e->checkauth;
-       my $circ = $e->retrieve_action_circulation($i) or return $e->event;
-       if( $e->requestor->id ne $circ->usr ) {
-               return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
-       }
-       return $circ;
+    my( $s, $c, $a, $i, $all_circ ) = @_;
+    my $e = new_editor(authtoken => $a);
+    return $e->event unless $e->checkauth;
+    my $method = $all_circ ?
+        'retrieve_action_all_circulation_slim' :
+        'retrieve_action_circulation';
+    my $circ = $e->$method($i) or return $e->event;
+    if( $e->requestor->id ne ($circ->usr || '') ) {
+        return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
+    }
+    return $circ;
 }
 
 
 __PACKAGE__->register_method(
-       method => 'fetch_circ_mods',
-       api_name => 'open-ils.circ.circ_modifier.retrieve.all');
+    method => 'fetch_circ_mods',
+    api_name => 'open-ils.circ.circ_modifier.retrieve.all');
 sub fetch_circ_mods {
     my($self, $conn, $args) = @_;
     my $mods = new_editor()->retrieve_all_config_circ_modifier;
@@ -80,16 +86,6 @@ sub fetch_circ_mods {
 }
 
 __PACKAGE__->register_method(
-       method => 'fetch_bill_types',
-       api_name => 'open-ils.circ.billing_type.retrieve.all');
-sub fetch_bill_types {
-       my $conf = OpenSRF::Utils::SettingsClient->new;
-       return $conf->config_value(
-               'apps', 'open-ils.circ', 'app_settings', 'billing_types', 'type' );
-}
-
-
-__PACKAGE__->register_method(
     method => 'ranged_billing_types',
     api_name => 'open-ils.circ.billing_type.ranged.retrieve.all');
 
@@ -108,21 +104,21 @@ sub ranged_billing_types {
 # Returns an array of {circ, record} hashes checked out by the user.
 # ------------------------------------------------------------------------
 __PACKAGE__->register_method(
-       method  => "checkouts_by_user",
-       api_name        => "open-ils.circ.actor.user.checked_out",
+    method  => "checkouts_by_user",
+    api_name    => "open-ils.circ.actor.user.checked_out",
     stream => 1,
-       NOTES           => <<"  NOTES");
-       Returns a list of open circulations as a pile of objects.  Each object
-       contains the relevant copy, circ, and record
-       NOTES
+    NOTES        => <<"    NOTES");
+    Returns a list of open circulations as a pile of objects.  Each object
+    contains the relevant copy, circ, and record
+    NOTES
 
 sub checkouts_by_user {
-       my($self, $client, $auth, $user_id) = @_;
+    my($self, $client, $auth, $user_id) = @_;
 
     my $e = new_editor(authtoken=>$auth);
     return $e->event unless $e->checkauth;
 
-       my $circ_ids = $e->search_action_circulation(
+    my $circ_ids = $e->search_action_circulation(
         {   usr => $user_id,
             checkin_time => undef,
             '-or' => [
@@ -169,106 +165,109 @@ sub checkouts_by_user {
 
 
 __PACKAGE__->register_method(
-       method  => "checkouts_by_user_slim",
-       api_name        => "open-ils.circ.actor.user.checked_out.slim",
-       NOTES           => <<"  NOTES");
-       Returns a list of open circulation objects
-       NOTES
+    method  => "checkouts_by_user_slim",
+    api_name    => "open-ils.circ.actor.user.checked_out.slim",
+    NOTES        => <<"    NOTES");
+    Returns a list of open circulation objects
+    NOTES
 
 # DEPRECAT ME?? XXX
 sub checkouts_by_user_slim {
-       my( $self, $client, $user_session, $user_id ) = @_;
+    my( $self, $client, $user_session, $user_id ) = @_;
 
-       my( $requestor, $target, $copy, $record, $evt );
+    my( $requestor, $target, $copy, $record, $evt );
 
-       ( $requestor, $target, $evt ) = 
-               $apputils->checkses_requestor( $user_session, $user_id, 'VIEW_CIRCULATIONS');
-       return $evt if $evt;
+    ( $requestor, $target, $evt ) = 
+        $apputils->checkses_requestor( $user_session, $user_id, 'VIEW_CIRCULATIONS');
+    return $evt if $evt;
 
-       $logger->debug( 'User ' . $requestor->id . 
-               " retrieving checked out items for user " . $target->id );
+    $logger->debug( 'User ' . $requestor->id . 
+        " retrieving checked out items for user " . $target->id );
 
-       # XXX Make the call correct..
-       return $apputils->simplereq(
-               'open-ils.cstore',
-               "open-ils.cstore.direct.action.open_circulation.search.atomic", 
-               { usr => $target->id, checkin_time => undef } );
-#              { usr => $target->id } );
+    # XXX Make the call correct..
+    return $apputils->simplereq(
+        'open-ils.cstore',
+        "open-ils.cstore.direct.action.open_circulation.search.atomic", 
+        { usr => $target->id, checkin_time => undef } );
+#       { usr => $target->id } );
 }
 
 
 __PACKAGE__->register_method(
-       method  => "checkouts_by_user_opac",
-       api_name        => "open-ils.circ.actor.user.checked_out.opac",);
+    method  => "checkouts_by_user_opac",
+    api_name    => "open-ils.circ.actor.user.checked_out.opac",);
 
 # XXX Deprecate Me
 sub checkouts_by_user_opac {
-       my( $self, $client, $auth, $user_id ) = @_;
+    my( $self, $client, $auth, $user_id ) = @_;
 
-       my $e = OpenILS::Utils::Editor->new( authtoken => $auth );
-       return $e->event unless $e->checkauth;
-       $user_id ||= $e->requestor->id;
-       return $e->event unless 
-               my $patron = $e->retrieve_actor_user($user_id);
+    my $e = new_editor( authtoken => $auth );
+    return $e->event unless $e->checkauth;
+    $user_id ||= $e->requestor->id;
+    return $e->event unless 
+        my $patron = $e->retrieve_actor_user($user_id);
 
-       my $data;
-       my $search = {usr => $user_id, stop_fines => undef};
+    my $data;
+    my $search = {usr => $user_id, stop_fines => undef};
 
-       if( $user_id ne $e->requestor->id ) {
-               $data = $e->search_action_circulation(
-                       $search, {checkperm=>1, permorg=>$patron->home_ou})
-                       or return $e->event;
+    if( $user_id ne $e->requestor->id ) {
+        $data = $e->search_action_circulation(
+            $search, {checkperm=>1, permorg=>$patron->home_ou})
+            or return $e->event;
 
-       } else {
-               $data = $e->search_action_circulation($search);
-       }
+    } else {
+        $data = $e->search_action_circulation($search);
+    }
 
-       return $data;
+    return $data;
 }
 
 
 __PACKAGE__->register_method(
-       method  => "title_from_transaction",
-       api_name        => "open-ils.circ.circ_transaction.find_title",
-       NOTES           => <<"  NOTES");
-       Returns a mods object for the title that is linked to from the 
-       copy from the hold that created the given transaction
-       NOTES
+    method  => "title_from_transaction",
+    api_name    => "open-ils.circ.circ_transaction.find_title",
+    NOTES        => <<"    NOTES");
+    Returns a mods object for the title that is linked to from the 
+    copy from the hold that created the given transaction
+    NOTES
 
 sub title_from_transaction {
-       my( $self, $client, $login_session, $transactionid ) = @_;
+    my( $self, $client, $login_session, $transactionid ) = @_;
 
-       my( $user, $circ, $title, $evt );
+    my( $user, $circ, $title, $evt );
 
-       ( $user, $evt ) = $apputils->checkses( $login_session );
-       return $evt if $evt;
+    ( $user, $evt ) = $apputils->checkses( $login_session );
+    return $evt if $evt;
 
-       ( $circ, $evt ) = $apputils->fetch_circulation($transactionid);
-       return $evt if $evt;
-       
-       ($title, $evt) = $apputils->fetch_record_by_copy($circ->target_copy);
-       return $evt if $evt;
+    ( $circ, $evt ) = $apputils->fetch_circulation($transactionid);
+    return $evt if $evt;
+    
+    ($title, $evt) = $apputils->fetch_record_by_copy($circ->target_copy);
+    return $evt if $evt;
 
-       return $apputils->record_to_mvr($title);
+    return $apputils->record_to_mvr($title);
 }
 
 __PACKAGE__->register_method(
-       method  => "staff_age_to_lost",
-       api_name        => "open-ils.circ.circulation.age_to_lost",
+    method  => "staff_age_to_lost",
+    api_name    => "open-ils.circ.circulation.age_to_lost",
     stream => 1,
-       signature       => q/
+    signature   => q/
         This fires a circ.staff_age_to_lost Action-Trigger event against all
         overdue circulations in scope of the specified context library and
         user profile, which effectively marks the associated items as Lost.
         This is likely to be done at the end of a semester in an academic
         library, etc.
-               @param auth
-               @param args : circ_lib, user_profile
-       /
+        @param auth
+        @param args : circ_lib, user_profile
+    /
 );
 
 sub staff_age_to_lost {
     my( $self, $conn, $auth, $args ) = @_;
+    my $e = new_editor(authtoken=>$auth);
+    return $e->event unless $e->checkauth;
+    return $e->event unless $e->allowed('SET_CIRC_LOST', $args->{'circ_lib'});
 
     my $orgs = $U->get_org_descendants($args->{'circ_lib'});
     my $profiles = $U->fetch_permission_group_descendants($args->{'user_profile'});
@@ -341,13 +340,13 @@ sub staff_age_to_lost {
 
 
 __PACKAGE__->register_method(
-       method  => "new_set_circ_lost",
-       api_name        => "open-ils.circ.circulation.set_lost",
-       signature       => q/
+    method  => "new_set_circ_lost",
+    api_name    => "open-ils.circ.circulation.set_lost",
+    signature   => q/
         Sets the copy and related open circulation to lost
-               @param auth
-               @param args : barcode
-       /
+        @param auth
+        @param args : barcode
+    /
 );
 
 
@@ -371,11 +370,42 @@ sub new_set_circ_lost {
     return 1;
 }
 
+__PACKAGE__->register_method(
+    method    => "update_latest_inventory",
+    api_name  => "open-ils.circ.circulation.update_latest_inventory");
+
+sub update_latest_inventory {
+    my( $self, $conn, $auth, $args ) = @_;
+    my $e = new_editor(authtoken=>$auth, xact=>1);
+    return $e->die_event unless $e->checkauth;
+
+    my $copies = $$args{copy_list};
+    foreach my $copyid (@$copies) {
+        my $copy = $e->retrieve_asset_copy($copyid);
+        my $alci = $e->search_asset_latest_inventory({copy => $copyid})->[0];
+
+        if($alci) {
+            $alci->inventory_date('now');
+            $alci->inventory_workstation($e->requestor->wsid);
+            $e->update_asset_latest_inventory($alci) or return $e->die_event;
+        } else {
+            my $alci = Fieldmapper::asset::latest_inventory->new;
+            $alci->inventory_date('now');
+            $alci->inventory_workstation($e->requestor->wsid);
+            $alci->copy($copy->id);
+            $e->create_asset_latest_inventory($alci) or return $e->die_event;
+        }
+
+        $copy->latest_inventory($alci);
+    }
+    $e->commit;
+    return 1;
+}
 
 __PACKAGE__->register_method(
-       method  => "set_circ_claims_returned",
-       api_name        => "open-ils.circ.circulation.set_claims_returned",
-       signature => {
+    method  => "set_circ_claims_returned",
+    api_name    => "open-ils.circ.circulation.set_claims_returned",
+    signature => {
         desc => q/Sets the circ for a given item as claims returned
                 If a backdate is provided, overdue fines will be voided
                 back to the backdate/,
@@ -390,9 +420,9 @@ __PACKAGE__->register_method(
 );
 
 __PACKAGE__->register_method(
-       method  => "set_circ_claims_returned",
-       api_name        => "open-ils.circ.circulation.set_claims_returned.override",
-       signature => {
+    method  => "set_circ_claims_returned",
+    api_name    => "open-ils.circ.circulation.set_claims_returned.override",
+    signature => {
         desc => q/This adds support for overrideing the configured max 
                 claims returned amount. 
                 @see open-ils.circ.circulation.set_claims_returned./,
@@ -400,11 +430,13 @@ __PACKAGE__->register_method(
 );
 
 sub set_circ_claims_returned {
-    my( $self, $conn, $auth, $args ) = @_;
+    my( $self, $conn, $auth, $args, $oargs ) = @_;
 
     my $e = new_editor(authtoken=>$auth, xact=>1);
     return $e->die_event unless $e->checkauth;
 
+    $oargs = { all => 1 } unless defined $oargs;
+
     my $barcode = $$args{barcode};
     my $backdate = $$args{backdate};
 
@@ -429,7 +461,7 @@ sub set_circ_claims_returned {
     # 0 means all attempts require an override
     if(defined $max_count and $patron->claims_returned_count >= $max_count) {
 
-        if($self->api_name =~ /override/) {
+        if($self->api_name =~ /override/ && ($oargs->{all} || grep { $_ eq 'PATRON_EXCEEDS_CLAIMS_RETURN_COUNT' } @{$oargs->{events}})) {
 
             # see if we're allowed to override
             return $e->die_event unless 
@@ -453,21 +485,21 @@ sub set_circ_claims_returned {
         or return $e->die_event;
 
     $circ->stop_fines(OILS_STOP_FINES_CLAIMSRETURNED);
-       $circ->stop_fines_time('now') unless $circ->stop_fines_time;
+    $circ->stop_fines_time('now') unless $circ->stop_fines_time;
 
     if( $backdate ) {
-        $backdate = cleanse_ISO8601($backdate);
+        $backdate = clean_ISO8601($backdate);
 
-        my $original_date = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($circ->due_date));
+        my $original_date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($circ->due_date));
         my $new_date = DateTime::Format::ISO8601->new->parse_datetime($backdate);
         $backdate = $new_date->ymd . 'T' . $original_date->strftime('%T%z');
 
         # clean it up once again; need a : in the timezone offset. E.g. -06:00 not -0600
-        $backdate = cleanse_ISO8601($backdate);
+        $backdate = clean_ISO8601($backdate);
 
         # make it look like the circ stopped at the cliams returned time
         $circ->stop_fines_time($backdate);
-        my $evt = OpenILS::Application::Circ::CircCommon->void_overdues($e, $circ, $backdate);
+        my $evt = OpenILS::Application::Circ::CircCommon->void_or_zero_overdues($e, $circ, {backdate => $backdate, note => 'System: OVERDUE REVERSED FOR CLAIMS-RETURNED', force_zero => 1});
         return $evt if $evt;
     }
 
@@ -475,21 +507,82 @@ sub set_circ_claims_returned {
 
     # see if there is a configured post-claims-return copy status
     if(my $stat = $U->ou_ancestor_setting_value($circ->circ_lib, 'circ.claim_return.copy_status')) {
-           $copy->status($stat);
-           $copy->edit_date('now');
-           $copy->editor($e->requestor->id);
-           $e->update_asset_copy($copy) or return $e->die_event;
+        $copy->status($stat);
+        $copy->edit_date('now');
+        $copy->editor($e->requestor->id);
+        $e->update_asset_copy($copy) or return $e->die_event;
+    }
+
+    # Check if the copy circ lib wants lost fees voided on claims
+    # returned.
+    if ($U->is_true($U->ou_ancestor_setting_value($copy->circ_lib, 'circ.void_lost_on_claimsreturned', $e))) {
+        my $result = OpenILS::Application::Circ::CircCommon->void_lost(
+            $e,
+            $circ,
+            3
+        );
+        if ($result) {
+            $e->rollback;
+            return $result;
+        }
+    }
+
+    # Check if the copy circ lib wants lost processing fees voided on
+    # claims returned.
+    if ($U->is_true($U->ou_ancestor_setting_value($copy->circ_lib, 'circ.void_lost_proc_fee_on_claimsreturned', $e))) {
+        my $result = OpenILS::Application::Circ::CircCommon->void_lost(
+            $e,
+            $circ,
+            4
+        );
+        if ($result) {
+            $e->rollback;
+            return $result;
+        }
+    }
+
+    # Check if the copy circ lib wants longoverdue fees voided on claims
+    # returned.
+    if ($U->is_true($U->ou_ancestor_setting_value($copy->circ_lib, 'circ.void_longoverdue_on_claimsreturned', $e))) {
+        my $result = OpenILS::Application::Circ::CircCommon->void_lost(
+            $e,
+            $circ,
+            10
+        );
+        if ($result) {
+            $e->rollback;
+            return $result;
+        }
+    }
+
+    # Check if the copy circ lib wants longoverdue processing fees voided on
+    # claims returned.
+    if ($U->is_true($U->ou_ancestor_setting_value($copy->circ_lib, 'circ.void_longoverdue_proc_fee_on_claimsreturned', $e))) {
+        my $result = OpenILS::Application::Circ::CircCommon->void_lost(
+            $e,
+            $circ,
+            11
+        );
+        if ($result) {
+            $e->rollback;
+            return $result;
+        }
     }
 
+    # Now that all data has been munged, do a no-op update of 
+    # the patron to force a change of the last_xact_id value.
+    $e->update_actor_user($e->retrieve_actor_user($circ->usr))
+        or return $e->die_event;
+
     $e->commit;
     return 1;
 }
 
 
 __PACKAGE__->register_method(
-       method  => "post_checkin_backdate_circ",
-       api_name        => "open-ils.circ.post_checkin_backdate",
-       signature => {
+    method  => "post_checkin_backdate_circ",
+    api_name    => "open-ils.circ.post_checkin_backdate",
+    signature => {
         desc => q/Back-date an already checked in circulation/,
         params => [
             {desc => 'Authentication token', type => 'string'},
@@ -501,10 +594,10 @@ __PACKAGE__->register_method(
 );
 
 __PACKAGE__->register_method(
-       method  => "post_checkin_backdate_circ",
-       api_name        => "open-ils.circ.post_checkin_backdate.batch",
+    method  => "post_checkin_backdate_circ",
+    api_name    => "open-ils.circ.post_checkin_backdate.batch",
     stream => 1,
-       signature => {
+    signature => {
         desc => q/@see open-ils.circ.post_checkin_backdate.  Batch mode/,
         params => [
             {desc => 'Authentication token', type => 'string'},
@@ -549,12 +642,12 @@ sub post_checkin_backdate_circ_impl {
         $backdate and $circ->checkin_time;
 
     # update the checkin and stop_fines times to reflect the new backdate
-    $circ->stop_fines_time(cleanse_ISO8601($backdate));
-    $circ->checkin_time(cleanse_ISO8601($backdate));
+    $circ->stop_fines_time(clean_ISO8601($backdate));
+    $circ->checkin_time(clean_ISO8601($backdate));
     $e->update_action_circulation($circ) or return $e->die_event;
 
     # now void the overdues "erased" by the back-dating
-    my $evt = OpenILS::Application::Circ::CircCommon->void_overdues($e, $circ, $backdate);
+    my $evt = OpenILS::Application::Circ::CircCommon->void_or_zero_overdues($e, $circ, {backdate => $backdate});
     return $evt if $evt;
 
     # If the circ was closed before and the balance owned !=0, re-open the transaction
@@ -568,18 +661,18 @@ sub post_checkin_backdate_circ_impl {
 
 
 __PACKAGE__->register_method (
-       method          => 'set_circ_due_date',
-       api_name                => 'open-ils.circ.circulation.due_date.update',
-       signature       => q/
-               Updates the due_date on the given circ
-               @param authtoken
-               @param circid The id of the circ to update
-               @param date The timestamp of the new due date
-       /
+    method      => 'set_circ_due_date',
+    api_name        => 'open-ils.circ.circulation.due_date.update',
+    signature   => q/
+        Updates the due_date on the given circ
+        @param authtoken
+        @param circid The id of the circ to update
+        @param date The timestamp of the new due date
+    /
 );
 
 sub set_circ_due_date {
-       my( $self, $conn, $auth, $circ_id, $date ) = @_;
+    my( $self, $conn, $auth, $circ_id, $date ) = @_;
 
     my $e = new_editor(xact=>1, authtoken=>$auth);
     return $e->die_event unless $e->checkauth;
@@ -587,15 +680,20 @@ sub set_circ_due_date {
         or return $e->die_event;
 
     return $e->die_event unless $e->allowed('CIRC_OVERRIDE_DUE_DATE', $circ->circ_lib);
-       $date = cleanse_ISO8601($date);
+    $date = clean_ISO8601($date);
 
     if (!(interval_to_seconds($circ->duration) % 86400)) { # duration is divisible by days
-        my $original_date = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($circ->due_date));
+        my $original_date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($circ->due_date));
         my $new_date = DateTime::Format::ISO8601->new->parse_datetime($date);
-        $date = cleanse_ISO8601( $new_date->ymd . 'T' . $original_date->strftime('%T%z') );
+
+        # since the new date may be coming in as UTC, convert it
+        # to the same time zone as the original due date so that
+        # ->ymd is more likely to yield the expected results
+        $new_date->set_time_zone($original_date->time_zone());
+        $date = clean_ISO8601( $new_date->ymd . 'T' . $original_date->strftime('%T%z') );
     }
 
-       $circ->due_date($date);
+    $circ->due_date($date);
     $e->update_action_circulation($circ) or return $e->die_event;
     $e->commit;
 
@@ -604,86 +702,86 @@ sub set_circ_due_date {
 
 
 __PACKAGE__->register_method(
-       method          => "create_in_house_use",
-       api_name                => 'open-ils.circ.in_house_use.create',
-       signature       =>      q/
-               Creates an in-house use action.
-               @param $authtoken The login session key
-               @param params A hash of params including
-                       'location' The org unit id where the in-house use occurs
-                       'copyid' The copy in question
-                       'count' The number of in-house uses to apply to this copy
-               @return An array of id's representing the id's of the newly created
-               in-house use objects or an event on an error
-       /);
+    method      => "create_in_house_use",
+    api_name        => 'open-ils.circ.in_house_use.create',
+    signature   =>  q/
+        Creates an in-house use action.
+        @param $authtoken The login session key
+        @param params A hash of params including
+            'location' The org unit id where the in-house use occurs
+            'copyid' The copy in question
+            'count' The number of in-house uses to apply to this copy
+        @return An array of id's representing the id's of the newly created
+        in-house use objects or an event on an error
+    /);
 
 __PACKAGE__->register_method(
-       method          => "create_in_house_use",
-       api_name                => 'open-ils.circ.non_cat_in_house_use.create',
+    method      => "create_in_house_use",
+    api_name        => 'open-ils.circ.non_cat_in_house_use.create',
 );
 
 
 sub create_in_house_use {
-       my( $self, $client, $auth, $params ) = @_;
-
-       my( $evt, $copy );
-       my $org                 = $params->{location};
-       my $copyid              = $params->{copyid};
-       my $count               = $params->{count} || 1;
-       my $nc_type             = $params->{non_cat_type};
-       my $use_time    = $params->{use_time} || 'now';
-
-       my $e = new_editor(xact=>1,authtoken=>$auth);
-       return $e->event unless $e->checkauth;
-       return $e->event unless $e->allowed('CREATE_IN_HOUSE_USE');
-
-       my $non_cat = 1 if $self->api_name =~ /non_cat/;
-
-       unless( $non_cat ) {
-               if( $copyid ) {
-                       $copy = $e->retrieve_asset_copy($copyid) or return $e->event;
-               } else {
-                       $copy = $e->search_asset_copy({barcode=>$params->{barcode}, deleted => 'f'})->[0]
-                               or return $e->event;
-                       $copyid = $copy->id;
-               }
-       }
-
-       if( $use_time ne 'now' ) {
-               $use_time = cleanse_ISO8601($use_time);
-               $logger->debug("in_house_use setting use time to $use_time");
-       }
-
-       my @ids;
-       for(1..$count) {
-
-               my $ihu;
-               my $method;
-               my $cmeth;
-
-               if($non_cat) {
-                       $ihu = Fieldmapper::action::non_cat_in_house_use->new;
-                       $ihu->item_type($nc_type);
-                       $method = 'open-ils.storage.direct.action.non_cat_in_house_use.create';
-                       $cmeth = "create_action_non_cat_in_house_use";
-
-               } else {
-                       $ihu = Fieldmapper::action::in_house_use->new;
-                       $ihu->item($copyid);
-                       $method = 'open-ils.storage.direct.action.in_house_use.create';
-                       $cmeth = "create_action_in_house_use";
-               }
-
-               $ihu->staff($e->requestor->id);
-               $ihu->org_unit($org);
-               $ihu->use_time($use_time);
-
-               $ihu = $e->$cmeth($ihu) or return $e->event;
-               push( @ids, $ihu->id );
-       }
-
-       $e->commit;
-       return \@ids;
+    my( $self, $client, $auth, $params ) = @_;
+
+    my( $evt, $copy );
+    my $org         = $params->{location};
+    my $copyid      = $params->{copyid};
+    my $count       = $params->{count} || 1;
+    my $nc_type     = $params->{non_cat_type};
+    my $use_time    = $params->{use_time} || 'now';
+
+    my $e = new_editor(xact=>1,authtoken=>$auth);
+    return $e->event unless $e->checkauth;
+    return $e->event unless $e->allowed('CREATE_IN_HOUSE_USE');
+
+    my $non_cat = 1 if $self->api_name =~ /non_cat/;
+
+    unless( $non_cat ) {
+        if( $copyid ) {
+            $copy = $e->retrieve_asset_copy($copyid) or return $e->event;
+        } else {
+            $copy = $e->search_asset_copy({barcode=>$params->{barcode}, deleted => 'f'})->[0]
+                or return $e->event;
+            $copyid = $copy->id;
+        }
+    }
+
+    if( $use_time ne 'now' ) {
+        $use_time = clean_ISO8601($use_time);
+        $logger->debug("in_house_use setting use time to $use_time");
+    }
+
+    my @ids;
+    for(1..$count) {
+
+        my $ihu;
+        my $method;
+        my $cmeth;
+
+        if($non_cat) {
+            $ihu = Fieldmapper::action::non_cat_in_house_use->new;
+            $ihu->item_type($nc_type);
+            $method = 'open-ils.storage.direct.action.non_cat_in_house_use.create';
+            $cmeth = "create_action_non_cat_in_house_use";
+
+        } else {
+            $ihu = Fieldmapper::action::in_house_use->new;
+            $ihu->item($copyid);
+            $method = 'open-ils.storage.direct.action.in_house_use.create';
+            $cmeth = "create_action_in_house_use";
+        }
+
+        $ihu->staff($e->requestor->id);
+        $ihu->org_unit($org);
+        $ihu->use_time($use_time);
+
+        $ihu = $e->$cmeth($ihu) or return $e->event;
+        push( @ids, $ihu->id );
+    }
+
+    $e->commit;
+    return \@ids;
 }
 
 
@@ -691,22 +789,22 @@ sub create_in_house_use {
 
 
 __PACKAGE__->register_method(
-       method  => "view_circs",
-       api_name        => "open-ils.circ.copy_checkout_history.retrieve",
-       notes           => q/
-               Retrieves the last X circs for a given copy
-               @param authtoken The login session key
-               @param copyid The copy to check
-               @param count How far to go back in the item history
-               @return An array of circ ids
-       /);
+    method  => "view_circs",
+    api_name    => "open-ils.circ.copy_checkout_history.retrieve",
+    notes       => q/
+        Retrieves the last X circs for a given copy
+        @param authtoken The login session key
+        @param copyid The copy to check
+        @param count How far to go back in the item history
+        @return An array of circ ids
+    /);
 
 # ----------------------------------------------------------------------
 # Returns $count most recent circs.  If count exceeds the configured 
 # max, use the configured max instead
 # ----------------------------------------------------------------------
 sub view_circs {
-       my( $self, $client, $authtoken, $copyid, $count ) = @_; 
+    my( $self, $client, $authtoken, $copyid, $count ) = @_; 
 
     my $e = new_editor(authtoken => $authtoken);
     return $e->event unless $e->checkauth;
@@ -732,470 +830,641 @@ sub view_circs {
         $count = 4 unless defined $count;
     }
 
-    return $e->search_action_circulation([
+    return $e->search_action_all_circulation_slim([
         {target_copy => $copyid}, 
-        {limit => $count, order_by => { circ => "xact_start DESC" }} 
+        {limit => $count, order_by => { aacs => "xact_start DESC" }} 
     ]);
 }
 
 
 __PACKAGE__->register_method(
-       method  => "circ_count",
-       api_name        => "open-ils.circ.circulation.count",
-       notes           => q/
-               Returns the number of times the item has circulated
-               @param copyid The copy to check
-       /);
+    method  => "circ_count",
+    api_name    => "open-ils.circ.circulation.count",
+    notes       => q/
+        Returns the number of times the item has circulated
+        @param copyid The copy to check
+    /);
 
 sub circ_count {
-       my( $self, $client, $copyid, $range ) = @_; 
-       my $e = OpenILS::Utils::Editor->new;
-       return $e->request('open-ils.storage.asset.copy.circ_count', $copyid, $range);
+    my( $self, $client, $copyid ) = @_; 
+
+    my $count = new_editor()->json_query({
+        select => {
+            circbyyr => [{
+                column => 'count',
+                transform => 'sum',
+                aggregate => 1
+            }]
+        },
+        from => 'circbyyr',
+        where => {'+circbyyr' => {copy => $copyid}}
+    })->[0]->{count};
+
+    return {
+        total => {
+            when => 'total',
+            count => $count
+        }
+    };
 }
 
 
-
 __PACKAGE__->register_method(
-       method          => 'fetch_notes',
-       authoritative   => 1,
-       api_name                => 'open-ils.circ.copy_note.retrieve.all',
-       signature       => q/
-               Returns an array of copy note objects.  
-               @param args A named hash of parameters including:
-                       authtoken       : Required if viewing non-public notes
-                       itemid          : The id of the item whose notes we want to retrieve
-                       pub                     : True if all the caller wants are public notes
-               @return An array of note objects
-       /);
+    method      => 'fetch_notes',
+    authoritative   => 1,
+    api_name        => 'open-ils.circ.copy_note.retrieve.all',
+    signature   => q/
+        Returns an array of copy note objects.  
+        @param args A named hash of parameters including:
+            authtoken   : Required if viewing non-public notes
+            itemid      : The id of the item whose notes we want to retrieve
+            pub         : True if all the caller wants are public notes
+        @return An array of note objects
+    /);
 
 __PACKAGE__->register_method(
-       method          => 'fetch_notes',
-       api_name                => 'open-ils.circ.call_number_note.retrieve.all',
-       signature       => q/@see open-ils.circ.copy_note.retrieve.all/);
+    method      => 'fetch_notes',
+    api_name        => 'open-ils.circ.call_number_note.retrieve.all',
+    signature   => q/@see open-ils.circ.copy_note.retrieve.all/);
 
 __PACKAGE__->register_method(
-       method          => 'fetch_notes',
-       api_name                => 'open-ils.circ.title_note.retrieve.all',
-       signature       => q/@see open-ils.circ.copy_note.retrieve.all/);
+    method      => 'fetch_notes',
+    api_name        => 'open-ils.circ.title_note.retrieve.all',
+    signature   => q/@see open-ils.circ.copy_note.retrieve.all/);
 
 
 # NOTE: VIEW_COPY/VOLUME/TITLE_NOTES perms should always be global
 sub fetch_notes {
-       my( $self, $connection, $args ) = @_;
-
-       my $id = $$args{itemid};
-       my $authtoken = $$args{authtoken};
-       my( $r, $evt);
-
-       if( $self->api_name =~ /copy/ ) {
-               if( $$args{pub} ) {
-                       return $U->cstorereq(
-                               'open-ils.cstore.direct.asset.copy_note.search.atomic',
-                               { owning_copy => $id, pub => 't' } );
-               } else {
-                       ( $r, $evt ) = $U->checksesperm($authtoken, 'VIEW_COPY_NOTES');
-                       return $evt if $evt;
-                       return $U->cstorereq(
-                               'open-ils.cstore.direct.asset.copy_note.search.atomic', {owning_copy => $id} );
-               }
-
-       } elsif( $self->api_name =~ /call_number/ ) {
-               if( $$args{pub} ) {
-                       return $U->cstorereq(
-                               'open-ils.cstore.direct.asset.call_number_note.search.atomic',
-                               { call_number => $id, pub => 't' } );
-               } else {
-                       ( $r, $evt ) = $U->checksesperm($authtoken, 'VIEW_VOLUME_NOTES');
-                       return $evt if $evt;
-                       return $U->cstorereq(
-                               'open-ils.cstore.direct.asset.call_number_note.search.atomic', { call_number => $id } );
-               }
-
-       } elsif( $self->api_name =~ /title/ ) {
-               if( $$args{pub} ) {
-                       return $U->cstorereq(
-                               'open-ils.cstore.direct.bilbio.record_note.search.atomic',
-                               { record => $id, pub => 't' } );
-               } else {
-                       ( $r, $evt ) = $U->checksesperm($authtoken, 'VIEW_TITLE_NOTES');
-                       return $evt if $evt;
-                       return $U->cstorereq(
-                               'open-ils.cstore.direct.biblio.record_note.search.atomic', { record => $id } );
-               }
-       }
-
-       return undef;
+    my( $self, $connection, $args ) = @_;
+
+    my $id = $$args{itemid};
+    my $authtoken = $$args{authtoken};
+    my( $r, $evt);
+
+    if( $self->api_name =~ /copy/ ) {
+        if( $$args{pub} ) {
+            return $U->cstorereq(
+                'open-ils.cstore.direct.asset.copy_note.search.atomic',
+                { owning_copy => $id, pub => 't' } );
+        } else {
+            ( $r, $evt ) = $U->checksesperm($authtoken, 'VIEW_COPY_NOTES');
+            return $evt if $evt;
+            return $U->cstorereq(
+                'open-ils.cstore.direct.asset.copy_note.search.atomic', {owning_copy => $id} );
+        }
+
+    } elsif( $self->api_name =~ /call_number/ ) {
+        if( $$args{pub} ) {
+            return $U->cstorereq(
+                'open-ils.cstore.direct.asset.call_number_note.search.atomic',
+                { call_number => $id, pub => 't' } );
+        } else {
+            ( $r, $evt ) = $U->checksesperm($authtoken, 'VIEW_VOLUME_NOTES');
+            return $evt if $evt;
+            return $U->cstorereq(
+                'open-ils.cstore.direct.asset.call_number_note.search.atomic', { call_number => $id } );
+        }
+
+    } elsif( $self->api_name =~ /title/ ) {
+        if( $$args{pub} ) {
+            return $U->cstorereq(
+                'open-ils.cstore.direct.bilbio.record_note.search.atomic',
+                { record => $id, pub => 't' } );
+        } else {
+            ( $r, $evt ) = $U->checksesperm($authtoken, 'VIEW_TITLE_NOTES');
+            return $evt if $evt;
+            return $U->cstorereq(
+                'open-ils.cstore.direct.biblio.record_note.search.atomic', { record => $id } );
+        }
+    }
+
+    return undef;
 }
 
 __PACKAGE__->register_method(
-       method  => 'has_notes',
-       api_name        => 'open-ils.circ.copy.has_notes');
+    method  => 'has_notes',
+    api_name    => 'open-ils.circ.copy.has_notes');
 __PACKAGE__->register_method(
-       method  => 'has_notes',
-       api_name        => 'open-ils.circ.call_number.has_notes');
+    method  => 'has_notes',
+    api_name    => 'open-ils.circ.call_number.has_notes');
 __PACKAGE__->register_method(
-       method  => 'has_notes',
-       api_name        => 'open-ils.circ.title.has_notes');
+    method  => 'has_notes',
+    api_name    => 'open-ils.circ.title.has_notes');
 
 
 sub has_notes {
-       my( $self, $conn, $authtoken, $id ) = @_;
-       my $editor = OpenILS::Utils::Editor->new(authtoken => $authtoken);
-       return $editor->event unless $editor->checkauth;
+    my( $self, $conn, $authtoken, $id ) = @_;
+    my $editor = new_editor(authtoken => $authtoken);
+    return $editor->event unless $editor->checkauth;
 
-       my $n = $editor->search_asset_copy_note(
-               {owning_copy=>$id}, {idlist=>1}) if $self->api_name =~ /copy/;
+    my $n = $editor->search_asset_copy_note(
+        {owning_copy=>$id}, {idlist=>1}) if $self->api_name =~ /copy/;
 
-       $n = $editor->search_asset_call_number_note(
-               {call_number=>$id}, {idlist=>1}) if $self->api_name =~ /call_number/;
+    $n = $editor->search_asset_call_number_note(
+        {call_number=>$id}, {idlist=>1}) if $self->api_name =~ /call_number/;
 
-       $n = $editor->search_biblio_record_note(
-               {record=>$id}, {idlist=>1}) if $self->api_name =~ /title/;
+    $n = $editor->search_biblio_record_note(
+        {record=>$id}, {idlist=>1}) if $self->api_name =~ /title/;
 
-       return scalar @$n;
+    return scalar @$n;
 }
 
 
 
 __PACKAGE__->register_method(
-       method          => 'create_copy_note',
-       api_name                => 'open-ils.circ.copy_note.create',
-       signature       => q/
-               Creates a new copy note
-               @param authtoken The login session key
-               @param note     The note object to create
-               @return The id of the new note object
-       /);
+    method      => 'create_copy_note',
+    api_name        => 'open-ils.circ.copy_note.create',
+    signature   => q/
+        Creates a new copy note
+        @param authtoken The login session key
+        @param note The note object to create
+        @return The id of the new note object
+    /);
 
 sub create_copy_note {
-       my( $self, $connection, $authtoken, $note ) = @_;
-
-       my $e = new_editor(xact=>1, authtoken=>$authtoken);
-       return $e->event unless $e->checkauth;
-       my $copy = $e->retrieve_asset_copy(
-               [
-                       $note->owning_copy,
-                       {       flesh => 1,
-                               flesh_fields => { 'acp' => ['call_number'] }
-                       }
-               ]
-       );
-
-       return $e->event unless 
-               $e->allowed('CREATE_COPY_NOTE', $copy->call_number->owning_lib);
-
-       $note->create_date('now');
-       $note->creator($e->requestor->id);
-       $note->pub( ($U->is_true($note->pub)) ? 't' : 'f' );
-       $note->clear_id;
-
-       $e->create_asset_copy_note($note) or return $e->event;
-       $e->commit;
-       return $note->id;
+    my( $self, $connection, $authtoken, $note ) = @_;
+
+    my $e = new_editor(xact=>1, authtoken=>$authtoken);
+    return $e->event unless $e->checkauth;
+    my $copy = $e->retrieve_asset_copy(
+        [
+            $note->owning_copy,
+            {   flesh => 1,
+                flesh_fields => { 'acp' => ['call_number'] }
+            }
+        ]
+    );
+
+    return $e->event unless 
+        $e->allowed('CREATE_COPY_NOTE', $copy->call_number->owning_lib);
+
+    $note->create_date('now');
+    $note->creator($e->requestor->id);
+    $note->pub( ($U->is_true($note->pub)) ? 't' : 'f' );
+    $note->clear_id;
+
+    $e->create_asset_copy_note($note) or return $e->event;
+    $e->commit;
+    return $note->id;
 }
 
 
 __PACKAGE__->register_method(
-       method          => 'delete_copy_note',
-       api_name                =>      'open-ils.circ.copy_note.delete',
-       signature       => q/
-               Deletes an existing copy note
-               @param authtoken The login session key
-               @param noteid The id of the note to delete
-               @return 1 on success - Event otherwise.
-               /);
+    method      => 'delete_copy_note',
+    api_name        =>  'open-ils.circ.copy_note.delete',
+    signature   => q/
+        Deletes an existing copy note
+        @param authtoken The login session key
+        @param noteid The id of the note to delete
+        @return 1 on success - Event otherwise.
+        /);
 sub delete_copy_note {
-       my( $self, $conn, $authtoken, $noteid ) = @_;
-
-       my $e = new_editor(xact=>1, authtoken=>$authtoken);
-       return $e->die_event unless $e->checkauth;
-
-       my $note = $e->retrieve_asset_copy_note([
-               $noteid,
-               { flesh => 2,
-                       flesh_fields => {
-                               'acpn' => [ 'owning_copy' ],
-                               'acp' => [ 'call_number' ],
-                       }
-               }
-       ]) or return $e->die_event;
-
-       if( $note->creator ne $e->requestor->id ) {
-               return $e->die_event unless 
-                       $e->allowed('DELETE_COPY_NOTE', $note->owning_copy->call_number->owning_lib);
-       }
-
-       $e->delete_asset_copy_note($note) or return $e->die_event;
-       $e->commit;
-       return 1;
+    my( $self, $conn, $authtoken, $noteid ) = @_;
+
+    my $e = new_editor(xact=>1, authtoken=>$authtoken);
+    return $e->die_event unless $e->checkauth;
+
+    my $note = $e->retrieve_asset_copy_note([
+        $noteid,
+        { flesh => 2,
+            flesh_fields => {
+                'acpn' => [ 'owning_copy' ],
+                'acp' => [ 'call_number' ],
+            }
+        }
+    ]) or return $e->die_event;
+
+    if( $note->creator ne $e->requestor->id ) {
+        return $e->die_event unless 
+            $e->allowed('DELETE_COPY_NOTE', $note->owning_copy->call_number->owning_lib);
+    }
+
+    $e->delete_asset_copy_note($note) or return $e->die_event;
+    $e->commit;
+    return 1;
+}
+
+__PACKAGE__->register_method(
+    method      => 'fetch_copy_tags',
+    authoritative   => 1,
+    api_name        => 'open-ils.circ.copy_tags.retrieve',
+    signature   => q/
+        Returns an array of publicly-visible copy tag objects.  
+        @param args A named hash of parameters including:
+            copy_id     : The id of the item whose notes we want to retrieve
+            tag_type    : Type of copy tags to retrieve, e.g., 'bookplate' (optional)
+            scope       : top of org subtree whose copy tags we want to see
+            depth       : how far down to look for copy tags (optional)
+        @return An array of copy tag objects
+    /);
+__PACKAGE__->register_method(
+    method      => 'fetch_copy_tags',
+    authoritative   => 1,
+    api_name        => 'open-ils.circ.copy_tags.retrieve.staff',
+    signature   => q/
+        Returns an array of all copy tag objects.  
+        @param args A named hash of parameters including:
+            authtoken   : Required to view non-public notes
+            copy_id     : The id of the item whose notes we want to retrieve (optional)
+            tag_type    : Type of copy tags to retrieve, e.g., 'bookplate'
+            scope       : top of org subtree whose copy tags we want to see
+            depth       : how far down to look for copy tags (optional)
+        @return An array of copy tag objects
+    /);
+
+sub fetch_copy_tags {
+    my ($self, $conn, $args) = @_;
+
+    my $org = $args->{scope};
+    my $depth = $args->{depth};
+
+    my $filter = {};
+    my $e;
+    if ($self->api_name =~ /\.staff/) {
+        my $authtoken = $args->{authtoken};
+        return new OpenILS::Event("BAD_PARAMS", "desc" => "authtoken required") unless defined $authtoken;    
+        $e = new_editor(authtoken => $args->{authtoken});
+        return $e->event unless $e->checkauth;
+        return $e->event unless $e->allowed('STAFF_LOGIN', $org);
+    } else {
+        $e = new_editor();
+        $filter->{pub} = 't';
+    }
+    $filter->{tag_type} = $args->{tag_type} if exists($args->{tag_type});
+    $filter->{'+acptcm'} = {
+        copy => $args->{copy_id}
+    };
+
+    # filter by owner of copy tag and depth
+    $filter->{owner} = {
+        in => {
+            select => {aou => [{
+                column => 'id',
+                transform => 'actor.org_unit_descendants',
+                result_field => 'id',
+                (defined($depth) ? ( params => [$depth] ) : ()),
+            }]},
+            from => 'aou',
+            where => {id => $org}
+        }
+    };
+
+    return $e->search_asset_copy_tag([$filter, { join => { acptcm => {} } }]);
 }
 
 
 __PACKAGE__->register_method(
-       method => 'age_hold_rules',
-       api_name        =>  'open-ils.circ.config.rules.age_hold_protect.retrieve.all',
+    method => 'age_hold_rules',
+    api_name    =>  'open-ils.circ.config.rules.age_hold_protect.retrieve.all',
 );
 
 sub age_hold_rules {
-       my( $self, $conn ) = @_;
-       return new_editor()->retrieve_all_config_rules_age_hold_protect();
+    my( $self, $conn ) = @_;
+    return new_editor()->retrieve_all_config_rules_age_hold_protect();
 }
 
 
 
 __PACKAGE__->register_method(
-       method => 'copy_details_barcode',
+    method => 'copy_details_barcode',
     authoritative => 1,
-       api_name => 'open-ils.circ.copy_details.retrieve.barcode');
+    api_name => 'open-ils.circ.copy_details.retrieve.barcode');
 sub copy_details_barcode {
-       my( $self, $conn, $auth, $barcode ) = @_;
+    my( $self, $conn, $auth, $barcode ) = @_;
     my $e = new_editor();
     my $cid = $e->search_asset_copy({barcode=>$barcode, deleted=>'f'}, {idlist=>1})->[0];
     return $e->event unless $cid;
-       return copy_details( $self, $conn, $auth, $cid );
+    return copy_details( $self, $conn, $auth, $cid );
 }
 
 
 __PACKAGE__->register_method(
-       method => 'copy_details',
-       api_name => 'open-ils.circ.copy_details.retrieve');
+    method => 'copy_details',
+    api_name => 'open-ils.circ.copy_details.retrieve');
 
 sub copy_details {
-       my( $self, $conn, $auth, $copy_id ) = @_;
-       my $e = new_editor(authtoken=>$auth);
-       return $e->event unless $e->checkauth;
-
-       my $flesh = { flesh => 1 };
-
-       my $copy = $e->retrieve_asset_copy(
-               [
-                       $copy_id,
-                       {
-                               flesh => 2,
-                               flesh_fields => {
-                                       acp => ['call_number','parts','peer_record_maps'],
-                                       acn => ['record','prefix','suffix','label_class']
-                               }
-                       }
-               ]) or return $e->event;
-
-
-       # De-flesh the copy for backwards compatibility
-       my $mvr;
-       my $vol = $copy->call_number;
-       if( ref $vol ) {
-               $copy->call_number($vol->id);
-               my $record = $vol->record;
-               if( ref $record ) {
-                       $vol->record($record->id);
-                       $mvr = $U->record_to_mvr($record);
-               }
-       }
-
-
-       my $hold = $e->search_action_hold_request(
-               { 
-                       current_copy            => $copy_id, 
-                       capture_time            => { "!=" => undef },
-                       fulfillment_time        => undef,
-                       cancel_time                     => undef,
-               }
-       )->[0];
-
-       OpenILS::Application::Circ::Holds::flesh_hold_transits([$hold]) if $hold;
-
-       my $transit = $e->search_action_transit_copy(
-               { target_copy => $copy_id, dest_recv_time => undef } )->[0];
-
-       # find the latest circ, open or closed
-       my $circ = $e->search_action_circulation(
-               [
-                       { target_copy => $copy_id },
-                       { 
-                flesh => 1,
+    my( $self, $conn, $auth, $copy_id ) = @_;
+    my $e = new_editor(authtoken=>$auth);
+    return $e->event unless $e->checkauth;
+
+    my $flesh = { flesh => 1 };
+
+    my $copy = $e->retrieve_asset_copy(
+        [
+            $copy_id,
+            {
+                flesh => 2,
                 flesh_fields => {
-                    circ => [
-                        'workstation',
-                        'checkin_workstation', 
-                        'duration_rule', 
-                        'max_fine_rule', 
-                        'recurring_fine_rule'
-                    ]
-                },
-                order_by => { circ => 'xact_start desc' }, 
-                limit => 1 
+                    acp => ['call_number','parts','peer_record_maps','floating'],
+                    acn => ['record','prefix','suffix','label_class']
+                }
             }
-               ]
-       )->[0];
-
-
-       return {
-               copy            => $copy,
-               hold            => $hold,
-               transit => $transit,
-               circ            => $circ,
-               volume  => $vol,
-               mvr             => $mvr,
-       };
+        ]) or return $e->event;
+
+
+    # De-flesh the copy for backwards compatibility
+    my $mvr;
+    my $vol = $copy->call_number;
+    if( ref $vol ) {
+        $copy->call_number($vol->id);
+        my $record = $vol->record;
+        if( ref $record ) {
+            $vol->record($record->id);
+            $mvr = $U->record_to_mvr($record);
+        }
+    }
+
+
+    my $hold = $e->search_action_hold_request(
+        { 
+            current_copy        => $copy_id, 
+            capture_time        => { "!=" => undef },
+            fulfillment_time    => undef,
+            cancel_time         => undef,
+        }
+    )->[0];
+
+    OpenILS::Application::Circ::Holds::flesh_hold_transits([$hold]) if $hold;
+
+    my $transit = $e->search_action_transit_copy(
+        { target_copy => $copy_id, dest_recv_time => undef, cancel_time => undef } )->[0];
+
+    # find the most recent circulation for the requested copy,
+    # be it active, completed, or aged.
+    my $circ = $e->search_action_all_circulation_slim([
+        { target_copy => $copy_id },
+        {
+            flesh => 1,
+            flesh_fields => {
+                aacs => [
+                    'workstation',
+                    'checkin_workstation',
+                    'duration_rule',
+                    'max_fine_rule',
+                    'recurring_fine_rule'
+                ],
+            },
+            order_by => { aacs => 'xact_start desc' },
+            limit => 1
+        }
+    ])->[0];
+
+    return {
+        copy    => $copy,
+        hold    => $hold,
+        transit => $transit,
+        circ    => $circ,
+        volume  => $vol,
+        mvr     => $mvr
+    };
 }
 
 
 
 
 __PACKAGE__->register_method(
-       method => 'mark_item',
-       api_name => 'open-ils.circ.mark_item_damaged',
-       signature       => q/
-               Changes the status of a copy to "damaged". Requires MARK_ITEM_DAMAGED permission.
-               @param authtoken The login session key
-               @param copy_id The ID of the copy to mark as damaged
-               @return 1 on success - Event otherwise.
-               /
+    method => 'mark_item',
+    api_name => 'open-ils.circ.mark_item_damaged',
+    signature   => q/
+        Changes the status of a copy to "damaged". Requires MARK_ITEM_DAMAGED permission.
+        @param authtoken The login session key
+        @param copy_id The ID of the copy to mark as damaged
+        @return 1 on success - Event otherwise.
+        /
 );
 __PACKAGE__->register_method(
-       method => 'mark_item',
-       api_name => 'open-ils.circ.mark_item_missing',
-       signature       => q/
-               Changes the status of a copy to "missing". Requires MARK_ITEM_MISSING permission.
-               @param authtoken The login session key
-               @param copy_id The ID of the copy to mark as missing 
-               @return 1 on success - Event otherwise.
-               /
+    method => 'mark_item',
+    api_name => 'open-ils.circ.mark_item_missing',
+    signature   => q/
+        Changes the status of a copy to "missing". Requires MARK_ITEM_MISSING permission.
+        @param authtoken The login session key
+        @param copy_id The ID of the copy to mark as missing 
+        @return 1 on success - Event otherwise.
+        /
 );
 __PACKAGE__->register_method(
-       method => 'mark_item',
-       api_name => 'open-ils.circ.mark_item_bindery',
-       signature       => q/
-               Changes the status of a copy to "bindery". Requires MARK_ITEM_BINDERY permission.
-               @param authtoken The login session key
-               @param copy_id The ID of the copy to mark as bindery
-               @return 1 on success - Event otherwise.
-               /
+    method => 'mark_item',
+    api_name => 'open-ils.circ.mark_item_bindery',
+    signature   => q/
+        Changes the status of a copy to "bindery". Requires MARK_ITEM_BINDERY permission.
+        @param authtoken The login session key
+        @param copy_id The ID of the copy to mark as bindery
+        @return 1 on success - Event otherwise.
+        /
 );
 __PACKAGE__->register_method(
-       method => 'mark_item',
-       api_name => 'open-ils.circ.mark_item_on_order',
-       signature       => q/
-               Changes the status of a copy to "on order". Requires MARK_ITEM_ON_ORDER permission.
-               @param authtoken The login session key
-               @param copy_id The ID of the copy to mark as on order 
-               @return 1 on success - Event otherwise.
-               /
+    method => 'mark_item',
+    api_name => 'open-ils.circ.mark_item_on_order',
+    signature   => q/
+        Changes the status of a copy to "on order". Requires MARK_ITEM_ON_ORDER permission.
+        @param authtoken The login session key
+        @param copy_id The ID of the copy to mark as on order 
+        @return 1 on success - Event otherwise.
+        /
 );
 __PACKAGE__->register_method(
-       method => 'mark_item',
-       api_name => 'open-ils.circ.mark_item_ill',
-       signature       => q/
-               Changes the status of a copy to "inter-library loan". Requires MARK_ITEM_ILL permission.
-               @param authtoken The login session key
-               @param copy_id The ID of the copy to mark as inter-library loan
-               @return 1 on success - Event otherwise.
-               /
+    method => 'mark_item',
+    api_name => 'open-ils.circ.mark_item_ill',
+    signature   => q/
+        Changes the status of a copy to "inter-library loan". Requires MARK_ITEM_ILL permission.
+        @param authtoken The login session key
+        @param copy_id The ID of the copy to mark as inter-library loan
+        @return 1 on success - Event otherwise.
+        /
 );
 __PACKAGE__->register_method(
-       method => 'mark_item',
-       api_name => 'open-ils.circ.mark_item_cataloging',
-       signature       => q/
-               Changes the status of a copy to "cataloging". Requires MARK_ITEM_CATALOGING permission.
-               @param authtoken The login session key
-               @param copy_id The ID of the copy to mark as cataloging 
-               @return 1 on success - Event otherwise.
-               /
+    method => 'mark_item',
+    api_name => 'open-ils.circ.mark_item_cataloging',
+    signature   => q/
+        Changes the status of a copy to "cataloging". Requires MARK_ITEM_CATALOGING permission.
+        @param authtoken The login session key
+        @param copy_id The ID of the copy to mark as cataloging 
+        @return 1 on success - Event otherwise.
+        /
 );
 __PACKAGE__->register_method(
-       method => 'mark_item',
-       api_name => 'open-ils.circ.mark_item_reserves',
-       signature       => q/
-               Changes the status of a copy to "reserves". Requires MARK_ITEM_RESERVES permission.
-               @param authtoken The login session key
-               @param copy_id The ID of the copy to mark as reserves
-               @return 1 on success - Event otherwise.
-               /
+    method => 'mark_item',
+    api_name => 'open-ils.circ.mark_item_reserves',
+    signature   => q/
+        Changes the status of a copy to "reserves". Requires MARK_ITEM_RESERVES permission.
+        @param authtoken The login session key
+        @param copy_id The ID of the copy to mark as reserves
+        @return 1 on success - Event otherwise.
+        /
 );
 __PACKAGE__->register_method(
-       method => 'mark_item',
-       api_name => 'open-ils.circ.mark_item_discard',
-       signature       => q/
-               Changes the status of a copy to "discard". Requires MARK_ITEM_DISCARD permission.
-               @param authtoken The login session key
-               @param copy_id The ID of the copy to mark as discard
-               @return 1 on success - Event otherwise.
-               /
+    method => 'mark_item',
+    api_name => 'open-ils.circ.mark_item_discard',
+    signature   => q/
+        Changes the status of a copy to "discard". Requires MARK_ITEM_DISCARD permission.
+        @param authtoken The login session key
+        @param copy_id The ID of the copy to mark as discard
+        @return 1 on success - Event otherwise.
+        /
 );
 
 sub mark_item {
-       my( $self, $conn, $auth, $copy_id, $args ) = @_;
-       my $e = new_editor(authtoken=>$auth, xact =>1);
-       return $e->die_event unless $e->checkauth;
+    my( $self, $conn, $auth, $copy_id, $args ) = @_;
     $args ||= {};
 
+    my $e = new_editor(authtoken=>$auth);
+    return $e->die_event unless $e->checkauth;
     my $copy = $e->retrieve_asset_copy([
         $copy_id,
-        {flesh => 1, flesh_fields => {'acp' => ['call_number']}}])
+        {flesh => 1, flesh_fields => {'acp' => ['call_number','status']}}])
             or return $e->die_event;
 
-    my $owning_lib = 
+    my $owning_lib =
         ($copy->call_number->id == OILS_PRECAT_CALL_NUMBER) ? 
             $copy->circ_lib : $copy->call_number->owning_lib;
 
-       my $perm = 'MARK_ITEM_MISSING';
-       my $stat = OILS_COPY_STATUS_MISSING;
-
-       if( $self->api_name =~ /damaged/ ) {
-               $perm = 'MARK_ITEM_DAMAGED';
-               $stat = OILS_COPY_STATUS_DAMAGED;
-        my $evt = handle_mark_damaged($e, $copy, $owning_lib, $args);
-        return $evt if $evt;
-
-       } elsif ( $self->api_name =~ /bindery/ ) {
-               $perm = 'MARK_ITEM_BINDERY';
-               $stat = OILS_COPY_STATUS_BINDERY;
-       } elsif ( $self->api_name =~ /on_order/ ) {
-               $perm = 'MARK_ITEM_ON_ORDER';
-               $stat = OILS_COPY_STATUS_ON_ORDER;
-       } elsif ( $self->api_name =~ /ill/ ) {
-               $perm = 'MARK_ITEM_ILL';
-               $stat = OILS_COPY_STATUS_ILL;
-       } elsif ( $self->api_name =~ /cataloging/ ) {
-               $perm = 'MARK_ITEM_CATALOGING';
-               $stat = OILS_COPY_STATUS_CATALOGING;
-       } elsif ( $self->api_name =~ /reserves/ ) {
-               $perm = 'MARK_ITEM_RESERVES';
-               $stat = OILS_COPY_STATUS_RESERVES;
-       } elsif ( $self->api_name =~ /discard/ ) {
-               $perm = 'MARK_ITEM_DISCARD';
-               $stat = OILS_COPY_STATUS_DISCARD;
-       }
+    my $evt; # For later.
+    my $perm = 'MARK_ITEM_MISSING';
+    my $stat = OILS_COPY_STATUS_MISSING;
+
+    if( $self->api_name =~ /damaged/ ) {
+        $perm = 'MARK_ITEM_DAMAGED';
+        $stat = OILS_COPY_STATUS_DAMAGED;
+    } elsif ( $self->api_name =~ /bindery/ ) {
+        $perm = 'MARK_ITEM_BINDERY';
+        $stat = OILS_COPY_STATUS_BINDERY;
+    } elsif ( $self->api_name =~ /on_order/ ) {
+        $perm = 'MARK_ITEM_ON_ORDER';
+        $stat = OILS_COPY_STATUS_ON_ORDER;
+    } elsif ( $self->api_name =~ /ill/ ) {
+        $perm = 'MARK_ITEM_ILL';
+        $stat = OILS_COPY_STATUS_ILL;
+    } elsif ( $self->api_name =~ /cataloging/ ) {
+        $perm = 'MARK_ITEM_CATALOGING';
+        $stat = OILS_COPY_STATUS_CATALOGING;
+    } elsif ( $self->api_name =~ /reserves/ ) {
+        $perm = 'MARK_ITEM_RESERVES';
+        $stat = OILS_COPY_STATUS_RESERVES;
+    } elsif ( $self->api_name =~ /discard/ ) {
+        $perm = 'MARK_ITEM_DISCARD';
+        $stat = OILS_COPY_STATUS_DISCARD;
+    }
 
     # caller may proceed if either perm is allowed
     return $e->die_event unless $e->allowed([$perm, 'UPDATE_COPY'], $owning_lib);
 
-       $copy->status($stat);
-       $copy->edit_date('now');
-       $copy->editor($e->requestor->id);
+    # Copy status checks.
+    if ($copy->status->id() == OILS_COPY_STATUS_CHECKED_OUT) {
+        # Items must be checked in before any attempt is made to change its status.
+        if ($args->{handle_checkin}) {
+            $evt = try_checkin($auth, $copy_id);
+        } else {
+            $evt = OpenILS::Event->new('ITEM_TO_MARK_CHECKED_OUT');
+        }
+    } elsif ($copy->status->id() == OILS_COPY_STATUS_IN_TRANSIT) {
+        # Items in transit need to have the transit aborted before being marked.
+        if ($args->{handle_transit}) {
+            $evt = try_abort_transit($auth, $copy_id);
+        } else {
+            $evt = OpenILS::Event->new('ITEM_TO_MARK_IN_TRANSIT');
+        }
+    } elsif ($U->is_true($copy->status->restrict_copy_delete()) && $self->api_name =~ /discard/) {
+        # Items with restrict_copy_delete status require the
+        # COPY_DELETE_WARNING.override permission to be marked for
+        # discard.
+        if ($args->{handle_copy_delete_warning}) {
+            $evt = $e->event unless $e->allowed(['COPY_DELETE_WARNING.override'], $owning_lib);
+        } else {
+            $evt = OpenILS::Event->new('COPY_DELETE_WARNING');
+        }
+    }
+    return $evt if $evt;
+
+    # Retrieving holds for later use.
+    my $holds = $e->search_action_hold_request([
+        {
+            current_copy => $copy->id,
+            fulfillment_time => undef,
+            cancel_time => undef,
+        },
+        {flesh=>1, flesh_fields=>{ahr=>['eligible_copies']}}
+    ]);
+
+    # Throw event if attempting to  mark discard the only copy to fill a hold.
+    if ($self->api_name =~ /discard/) {
+        if (!$args->{handle_last_hold_copy}) {
+            for my $hold (@$holds) {
+                my $eligible = $hold->eligible_copies();
+                if (!defined($eligible) || scalar(@{$eligible}) < 2) {
+                    $evt = OpenILS::Event->new('ITEM_TO_MARK_LAST_HOLD_COPY');
+                    last;
+                }
+            }
+        }
+    }
+    return $evt if $evt;
+
+    # Things below here require a transaction and there is nothing left to interfere with it.
+    $e->xact_begin;
+
+    # Handle extra mark damaged charges, etc.
+    if ($self->api_name =~ /damaged/) {
+        $evt = handle_mark_damaged($e, $copy, $owning_lib, $args);
+        return $evt if $evt;
+    }
 
-       $e->update_asset_copy($copy) or return $e->die_event;
+    # Mark the copy.
+    $copy->status($stat);
+    $copy->edit_date('now');
+    $copy->editor($e->requestor->id);
 
-       my $holds = $e->search_action_hold_request(
-               { 
-                       current_copy => $copy->id,
-                       fulfillment_time => undef,
-                       cancel_time => undef,
-               }
-       );
+    $e->update_asset_copy($copy) or return $e->die_event;
 
-       $e->commit;
+    $e->commit;
 
-       if( $self->api_name =~ /damaged/ ) {
+    if( $self->api_name =~ /damaged/ ) {
         # now that we've committed the changes, create related A/T events
         my $ses = OpenSRF::AppSession->create('open-ils.trigger');
         $ses->request('open-ils.trigger.event.autocreate', 'damaged', $copy, $owning_lib);
     }
 
-       $logger->debug("resetting holds that target the marked copy");
-       OpenILS::Application::Circ::Holds->_reset_hold($e->requestor, $_) for @$holds;
+    $logger->debug("resetting holds that target the marked copy");
+    OpenILS::Application::Circ::Holds->_reset_hold($e->requestor, $_) for @$holds;
+
+    return 1;
+}
+
+sub try_checkin {
+    my($auth, $copy_id) = @_;
+
+    my $checkin = $U->simplereq(
+        'open-ils.circ',
+        'open-ils.circ.checkin.override',
+        $auth, {
+            copy_id => $copy_id,
+            noop => 1
+        }
+    );
+    if(ref $checkin ne 'ARRAY') { $checkin = [$checkin]; }
+
+    my $evt_code = $checkin->[0]->{textcode};
+    $logger->info("try_checkin() received event: $evt_code");
+
+    if($evt_code eq 'SUCCESS' || $evt_code eq 'NO_CHANGE') {
+        $logger->info('try_checkin() successful checkin');
+        return undef;
+    } else {
+        $logger->warn('try_checkin() un-successful checkin');
+        return $checkin;
+    }
+}
+
+sub try_abort_transit {
+    my ($auth, $copy_id) = @_;
 
-       return 1;
+    my $abort = $U->simplereq(
+        'open-ils.circ',
+        'open-ils.circ.transit.abort',
+        $auth, {copyid => $copy_id}
+    );
+    # Above returns 1 or an event.
+    return $abort if (ref $abort);
+    return undef;
 }
 
 sub handle_mark_damaged {
@@ -1265,7 +1534,7 @@ sub handle_mark_damaged {
         # the assumption is that you would not void the overdues unless you 
         # were also charging for the item and/or applying a processing fee
         if($void_overdue) {
-            my $evt = OpenILS::Application::Circ::CircCommon->void_overdues($e, $circ);
+            my $evt = OpenILS::Application::Circ::CircCommon->void_or_zero_overdues($e, $circ, {note => 'System: OVERDUE REVERSED FOR DAMAGE CHARGE'});
             return $evt if $evt;
         }
 
@@ -1275,7 +1544,8 @@ sub handle_mark_damaged {
         my $ses = OpenSRF::AppSession->create('open-ils.trigger');
         $ses->request('open-ils.trigger.event.autocreate', 'checkout.damaged', $circ, $circ->circ_lib);
 
-        return undef;
+        my $evt2 = OpenILS::Utils::Penalty->calculate_penalties($e, $circ->usr->id, $e->requestor->ws_ou);
+        return $evt2 if $evt2;
 
     } else {
         return OpenILS::Event->new('DAMAGE_CHARGE', 
@@ -1293,7 +1563,7 @@ sub handle_mark_damaged {
 __PACKAGE__->register_method(
     method => 'mark_item_missing_pieces',
     api_name => 'open-ils.circ.mark_item_missing_pieces',
-    signature  => q/
+    signature   => q/
         Changes the status of a copy to "damaged" or to a custom status based on the 
         circ.missing_pieces.copy_status org unit setting. Requires MARK_ITEM_MISSING_PIECES
         permission.
@@ -1304,81 +1574,102 @@ __PACKAGE__->register_method(
 );
 
 sub mark_item_missing_pieces {
-       my( $self, $conn, $auth, $copy_id, $args ) = @_;
+    my( $self, $conn, $auth, $copy_id, $args ) = @_;
     ### FIXME: We're starting a transaction here, but we're doing a lot of things outside of the transaction
-       my $e = new_editor(authtoken=>$auth, xact =>1);
-       return $e->die_event unless $e->checkauth;
+    ### FIXME: Even better, we're going to use two transactions, the first to affect pertinent holds before checkout can
+
+    my $e2 = new_editor(authtoken=>$auth, xact =>1);
+    return $e2->die_event unless $e2->checkauth;
     $args ||= {};
 
-    my $copy = $e->retrieve_asset_copy([
+    my $copy = $e2->retrieve_asset_copy([
         $copy_id,
         {flesh => 1, flesh_fields => {'acp' => ['call_number']}}])
-            or return $e->die_event;
+            or return $e2->die_event;
 
     my $owning_lib = 
         ($copy->call_number->id == OILS_PRECAT_CALL_NUMBER) ? 
             $copy->circ_lib : $copy->call_number->owning_lib;
 
-    return $e->die_event unless $e->allowed('MARK_ITEM_MISSING_PIECES', $owning_lib);
+    return $e2->die_event unless $e2->allowed('MARK_ITEM_MISSING_PIECES', $owning_lib);
 
     #### grab the last circulation
-    my $circ = $e->search_action_circulation([
+    my $circ = $e2->search_action_circulation([
         {   target_copy => $copy->id}, 
         {   limit => 1, 
             order_by => {circ => "xact_start DESC"}
         }
     ])->[0];
 
-    if ($circ) {
-        if (! $circ->checkin_time) { # if circ active, attempt renew
-            my ($res) = $self->method_lookup('open-ils.circ.renew')->run($e->authtoken,{'copy_id'=>$circ->target_copy});
-            if (ref $res ne 'ARRAY') { $res = [ $res ]; }
-            if ( $res->[0]->{textcode} eq 'SUCCESS' ) {
-                $circ = $res->[0]->{payload}{'circ'};
-                $circ->target_copy( $copy->id );
-                $logger->info('open-ils.circ.mark_item_missing_pieces: successful renewal');
-            } else {
-                $logger->info('open-ils.circ.mark_item_missing_pieces: non-successful renewal');
-            }
-        } else {
+    if (!$circ) {
+        $logger->info('open-ils.circ.mark_item_missing_pieces: no previous checkout');
+        $e2->rollback;
+        return OpenILS::Event->new('ACTION_CIRCULATION_NOT_FOUND',{'copy'=>$copy});
+    }
+
+    my $holds = $e2->search_action_hold_request(
+        { 
+            current_copy => $copy->id,
+            fulfillment_time => undef,
+            cancel_time => undef,
+        }
+    );
+
+    $logger->debug("resetting holds that target the marked copy");
+    OpenILS::Application::Circ::Holds->_reset_hold($e2->requestor, $_) for @$holds;
 
-            my $co_params = {
-                'copy_id'=>$circ->target_copy,
-                'patron_id'=>$circ->usr,
-                'skip_deposit_fee'=>1,
-                'skip_rental_fee'=>1
-            };
+    
+    if (! $e2->commit) {
+        return $e2->die_event;
+    }
 
-            if ($U->ou_ancestor_setting_value($e->requestor->ws_ou, 'circ.block_renews_for_holds')) {
+    my $e = new_editor(authtoken=>$auth, xact =>1);
+    return $e->die_event unless $e->checkauth;
 
-                my ($hold, undef, $retarget) = $holdcode->find_nearest_permitted_hold(
-                    $e, $copy, $e->requestor, 1 );
+    if (! $circ->checkin_time) { # if circ active, attempt renew
+        my ($res) = $self->method_lookup('open-ils.circ.renew')->run($e->authtoken,{'copy_id'=>$circ->target_copy});
+        if (ref $res ne 'ARRAY') { $res = [ $res ]; }
+        if ( $res->[0]->{textcode} eq 'SUCCESS' ) {
+            $circ = $res->[0]->{payload}{'circ'};
+            $circ->target_copy( $copy->id );
+            $logger->info('open-ils.circ.mark_item_missing_pieces: successful renewal');
+        } else {
+            $logger->info('open-ils.circ.mark_item_missing_pieces: non-successful renewal');
+        }
+    } else {
 
-                if ($hold) { # needed for hold? then due now
+        my $co_params = {
+            'copy_id'=>$circ->target_copy,
+            'patron_id'=>$circ->usr,
+            'skip_deposit_fee'=>1,
+            'skip_rental_fee'=>1
+        };
 
-                    $logger->info('open-ils.circ.mark_item_missing_pieces: item needed for hold, shortening due date');
-                    my $due_date = DateTime->now(time_zone => 'local');
-                    $co_params->{'due_date'} = cleanse_ISO8601( $due_date->strftime('%FT%T%z') );
-                } else {
-                    $logger->info('open-ils.circ.mark_item_missing_pieces: item not needed for hold');
-                }
-            }
+        if ($U->ou_ancestor_setting_value($e->requestor->ws_ou, 'circ.block_renews_for_holds')) {
 
-            my ($res) = $self->method_lookup('open-ils.circ.checkout.full.override')->run($e->authtoken,$co_params);
-            if (ref $res ne 'ARRAY') { $res = [ $res ]; }
-            if ( $res->[0]->{textcode} eq 'SUCCESS' ) {
-                $logger->info('open-ils.circ.mark_item_missing_pieces: successful checkout');
-                $circ = $res->[0]->{payload}{'circ'};
+            my ($hold, undef, $retarget) = $holdcode->find_nearest_permitted_hold(
+                $e, $copy, $e->requestor, 1 );
+
+            if ($hold) { # needed for hold? then due now
+
+                $logger->info('open-ils.circ.mark_item_missing_pieces: item needed for hold, shortening due date');
+                my $due_date = DateTime->now(time_zone => 'local');
+                $co_params->{'due_date'} = clean_ISO8601( $due_date->strftime('%FT%T%z') );
             } else {
-                $logger->info('open-ils.circ.mark_item_missing_pieces: non-successful checkout');
-                $e->rollback;
-                return $res;
+                $logger->info('open-ils.circ.mark_item_missing_pieces: item not needed for hold');
             }
         }
-    } else {
-        $logger->info('open-ils.circ.mark_item_missing_pieces: no previous checkout');
-        $e->rollback;
-        return OpenILS::Event->new('ACTION_CIRCULATION_NOT_FOUND',{'copy'=>$copy});
+
+        my ($res) = $self->method_lookup('open-ils.circ.checkout.full.override')->run($e->authtoken,$co_params,{ all => 1 });
+        if (ref $res ne 'ARRAY') { $res = [ $res ]; }
+        if ( $res->[0]->{textcode} eq 'SUCCESS' ) {
+            $logger->info('open-ils.circ.mark_item_missing_pieces: successful checkout');
+            $circ = $res->[0]->{payload}{'circ'};
+        } else {
+            $logger->info('open-ils.circ.mark_item_missing_pieces: non-successful checkout');
+            $e->rollback;
+            return $res;
+        }
     }
 
     ### Update the item status
@@ -1390,24 +1681,13 @@ sub mark_item_missing_pieces {
     my $ses = OpenSRF::AppSession->create('open-ils.trigger');
     $ses->request('open-ils.trigger.event.autocreate', 'missing_pieces', $copy, $owning_lib);
 
-       $copy->status($stat);
-       $copy->edit_date('now');
-       $copy->editor($e->requestor->id);
+    $copy->status($stat);
+    $copy->edit_date('now');
+    $copy->editor($e->requestor->id);
 
-       $e->update_asset_copy($copy) or return $e->die_event;
+    $e->update_asset_copy($copy) or return $e->die_event;
 
-       my $holds = $e->search_action_hold_request(
-               { 
-                       current_copy => $copy->id,
-                       fulfillment_time => undef,
-                       cancel_time => undef,
-               }
-       );
-
-    $logger->debug("resetting holds that target the marked copy");
-    OpenILS::Application::Circ::Holds->_reset_hold($e->requestor, $_) for @$holds;
-
-       if ($e->commit) {
+    if ($e->commit) {
 
         my $ses = OpenSRF::AppSession->create('open-ils.trigger');
         $ses->request('open-ils.trigger.event.autocreate', 'circ.missing_pieces', $circ, $circ->circ_lib);
@@ -1432,92 +1712,92 @@ sub mark_item_missing_pieces {
 
 # ----------------------------------------------------------------------
 __PACKAGE__->register_method(
-       method => 'magic_fetch',
-       api_name => 'open-ils.agent.fetch'
+    method => 'magic_fetch',
+    api_name => 'open-ils.agent.fetch'
 );
 
 my @FETCH_ALLOWED = qw/ aou aout acp acn bre /;
 
 sub magic_fetch {
-       my( $self, $conn, $auth, $args ) = @_;
-       my $e = new_editor( authtoken => $auth );
-       return $e->event unless $e->checkauth;
+    my( $self, $conn, $auth, $args ) = @_;
+    my $e = new_editor( authtoken => $auth );
+    return $e->event unless $e->checkauth;
 
-       my $hint = $$args{hint};
-       my $id  = $$args{id};
+    my $hint = $$args{hint};
+    my $id  = $$args{id};
 
-       # Is the call allowed to fetch this type of object?
-       return undef unless grep { $_ eq $hint } @FETCH_ALLOWED;
+    # Is the call allowed to fetch this type of object?
+    return undef unless grep { $_ eq $hint } @FETCH_ALLOWED;
 
-       # Find the class the implements the given hint
-       my ($class) = grep { 
-               $Fieldmapper::fieldmap->{$_}{hint} eq $hint } Fieldmapper->classes;
+    # Find the class the implements the given hint
+    my ($class) = grep { 
+        $Fieldmapper::fieldmap->{$_}{hint} eq $hint } Fieldmapper->classes;
 
-       $class =~ s/Fieldmapper:://og;
-       $class =~ s/::/_/og;
-       my $method = "retrieve_$class";
+    $class =~ s/Fieldmapper:://og;
+    $class =~ s/::/_/og;
+    my $method = "retrieve_$class";
 
-       my $obj = $e->$method($id) or return $e->event;
-       return $obj;
+    my $obj = $e->$method($id) or return $e->event;
+    return $obj;
 }
 # ----------------------------------------------------------------------
 
 
 __PACKAGE__->register_method(
-       method  => "fleshed_circ_retrieve",
+    method  => "fleshed_circ_retrieve",
     authoritative => 1,
-       api_name        => "open-ils.circ.fleshed.retrieve",);
+    api_name    => "open-ils.circ.fleshed.retrieve",);
 
 sub fleshed_circ_retrieve {
-       my( $self, $client, $id ) = @_;
-       my $e = new_editor();
-       my $circ = $e->retrieve_action_circulation(
-               [
-                       $id,
-                       
-                               flesh                           => 4,
-                               flesh_fields    => { 
-                                       circ => [ qw/ target_copy / ],
-                                       acp => [ qw/ location status stat_cat_entry_copy_maps notes age_protect call_number / ],
-                                       ascecm => [ qw/ stat_cat stat_cat_entry / ],
-                                       acn => [ qw/ record / ],
-                               }
-                       }
-               ]
-       ) or return $e->event;
-       
-       my $copy = $circ->target_copy;
-       my $vol = $copy->call_number;
-       my $rec = $circ->target_copy->call_number->record;
-
-       $vol->record($rec->id);
-       $copy->call_number($vol->id);
-       $circ->target_copy($copy->id);
-
-       my $mvr;
-
-       if( $rec->id == OILS_PRECAT_RECORD ) {
-               $rec = undef;
-               $vol = undef;
-       } else { 
-               $mvr = $U->record_to_mvr($rec);
-               $rec->marc(''); # drop the bulky marc data
-       }
-
-       return {
-               circ => $circ,
-               copy => $copy,
-               volume => $vol,
-               record => $rec,
-               mvr => $mvr,
-       };
+    my( $self, $client, $id ) = @_;
+    my $e = new_editor();
+    my $circ = $e->retrieve_action_circulation(
+        [
+            $id,
+            { 
+                flesh               => 4,
+                flesh_fields    => { 
+                    circ => [ qw/ target_copy / ],
+                    acp => [ qw/ location status stat_cat_entry_copy_maps notes age_protect call_number parts / ],
+                    ascecm => [ qw/ stat_cat stat_cat_entry / ],
+                    acn => [ qw/ record / ],
+                }
+            }
+        ]
+    ) or return $e->event;
+    
+    my $copy = $circ->target_copy;
+    my $vol = $copy->call_number;
+    my $rec = $circ->target_copy->call_number->record;
+
+    $vol->record($rec->id);
+    $copy->call_number($vol->id);
+    $circ->target_copy($copy->id);
+
+    my $mvr;
+
+    if( $rec->id == OILS_PRECAT_RECORD ) {
+        $rec = undef;
+        $vol = undef;
+    } else { 
+        $mvr = $U->record_to_mvr($rec);
+        $rec->marc(''); # drop the bulky marc data
+    }
+
+    return {
+        circ => $circ,
+        copy => $copy,
+        volume => $vol,
+        record => $rec,
+        mvr => $mvr,
+    };
 }
 
 
 
 __PACKAGE__->register_method(
-       method  => "test_batch_circ_events",
-       api_name        => "open-ils.circ.trigger_event_by_def_and_barcode.fire"
+    method  => "test_batch_circ_events",
+    api_name    => "open-ils.circ.trigger_event_by_def_and_barcode.fire"
 );
 
 #  method for testing the behavior of a given event definition
@@ -1525,7 +1805,7 @@ sub test_batch_circ_events {
     my($self, $conn, $auth, $event_def, $barcode) = @_;
 
     my $e = new_editor(authtoken => $auth);
-       return $e->event unless $e->checkauth;
+    return $e->event unless $e->checkauth;
     return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
 
     my $copy = $e->search_asset_copy({barcode => $barcode, deleted => 'f'})->[0]
@@ -1542,8 +1822,8 @@ sub test_batch_circ_events {
 
 
 __PACKAGE__->register_method(
-       method  => "fire_circ_events", 
-       api_name        => "open-ils.circ.fire_circ_trigger_events",
+    method  => "fire_circ_events", 
+    api_name    => "open-ils.circ.fire_circ_trigger_events",
     signature => q/
         General event def runner for circ objects.  If no event def ID
         is provided, the hook will be used to find the best event_def
@@ -1552,8 +1832,8 @@ __PACKAGE__->register_method(
 );
 
 __PACKAGE__->register_method(
-       method  => "fire_circ_events", 
-       api_name        => "open-ils.circ.fire_hold_trigger_events",
+    method  => "fire_circ_events", 
+    api_name    => "open-ils.circ.fire_hold_trigger_events",
     signature => q/
         General event def runner for hold objects.  If no event def ID
         is provided, the hook will be used to find the best event_def
@@ -1562,8 +1842,8 @@ __PACKAGE__->register_method(
 );
 
 __PACKAGE__->register_method(
-       method  => "fire_circ_events", 
-       api_name        => "open-ils.circ.fire_user_trigger_events",
+    method  => "fire_circ_events", 
+    api_name    => "open-ils.circ.fire_user_trigger_events",
     signature => q/
         General event def runner for user objects.  If no event def ID
         is provided, the hook will be used to find the best event_def
@@ -1576,7 +1856,7 @@ sub fire_circ_events {
     my($self, $conn, $auth, $org_id, $event_def, $hook, $granularity, $target_ids, $user_data) = @_;
 
     my $e = new_editor(authtoken => $auth, xact => 1);
-       return $e->event unless $e->checkauth;
+    return $e->event unless $e->checkauth;
 
     my $targets;
 
@@ -1600,10 +1880,10 @@ sub fire_circ_events {
 }
 
 __PACKAGE__->register_method(
-       method  => "user_payments_list",
-       api_name        => "open-ils.circ.user_payments.filtered.batch",
+    method  => "user_payments_list",
+    api_name    => "open-ils.circ.user_payments.filtered.batch",
     stream => 1,
-       signature => {
+    signature => {
         desc => q/Returns a fleshed, date-limited set of all payments a user
                 has made.  By default, ordered by payment date.  Optionally
                 ordered by other columns in the top-level "mp" object/,
@@ -1676,10 +1956,10 @@ sub user_payments_list {
 
 
 __PACKAGE__->register_method(
-       method  => "retrieve_circ_chain",
-       api_name        => "open-ils.circ.renewal_chain.retrieve_by_circ",
+    method  => "retrieve_circ_chain",
+    api_name    => "open-ils.circ.renewal_chain.retrieve_by_circ",
     stream => 1,
-       signature => {
+    signature => {
         desc => q/Given a circulation, this returns all circulation objects
                 that are part of the same chain of renewals./,
         params => [
@@ -1691,9 +1971,9 @@ __PACKAGE__->register_method(
 );
 
 __PACKAGE__->register_method(
-       method  => "retrieve_circ_chain",
-       api_name        => "open-ils.circ.renewal_chain.retrieve_by_circ.summary",
-       signature => {
+    method  => "retrieve_circ_chain",
+    api_name    => "open-ils.circ.renewal_chain.retrieve_by_circ.summary",
+    signature => {
         desc => q/Given a circulation, this returns a summary of the circulation objects
                 that are part of the same chain of renewals./,
         params => [
@@ -1709,17 +1989,17 @@ sub retrieve_circ_chain {
 
     my $e = new_editor(authtoken => $auth);
     return $e->event unless $e->checkauth;
-       return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
+    return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
 
     if($self->api_name =~ /summary/) {
         return $U->create_circ_chain_summary($e, $circ_id);
 
     } else {
 
-        my $chain = $e->json_query({from => ['action.circ_chain', $circ_id]});
+        my $chain = $e->json_query({from => ['action.all_circ_chain', $circ_id]});
 
         for my $circ_info (@$chain) {
-            my $circ = Fieldmapper::action::circulation->new;
+            my $circ = Fieldmapper::action::all_circulation_slim->new;
             $circ->$_($circ_info->{$_}) for keys %$circ_info;
             $conn->respond($circ);
         }
@@ -1729,10 +2009,10 @@ sub retrieve_circ_chain {
 }
 
 __PACKAGE__->register_method(
-       method  => "retrieve_prev_circ_chain",
-       api_name        => "open-ils.circ.prev_renewal_chain.retrieve_by_circ",
+    method  => "retrieve_prev_circ_chain",
+    api_name    => "open-ils.circ.prev_renewal_chain.retrieve_by_circ",
     stream => 1,
-       signature => {
+    signature => {
         desc => q/Given a circulation, this returns all circulation objects
                 that are part of the previous chain of renewals./,
         params => [
@@ -1744,9 +2024,9 @@ __PACKAGE__->register_method(
 );
 
 __PACKAGE__->register_method(
-       method  => "retrieve_prev_circ_chain",
-       api_name        => "open-ils.circ.prev_renewal_chain.retrieve_by_circ.summary",
-       signature => {
+    method  => "retrieve_prev_circ_chain",
+    api_name    => "open-ils.circ.prev_renewal_chain.retrieve_by_circ.summary",
+    signature => {
         desc => q/Given a circulation, this returns a summary of the circulation objects
                 that are part of the previous chain of renewals./,
         params => [
@@ -1762,53 +2042,53 @@ sub retrieve_prev_circ_chain {
 
     my $e = new_editor(authtoken => $auth);
     return $e->event unless $e->checkauth;
-       return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
+    return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
 
-    if($self->api_name =~ /summary/) {
-        my $first_circ = $e->json_query({from => ['action.circ_chain', $circ_id]})->[0];
-        my $target_copy = $$first_circ{'target_copy'};
-        my $usr = $$first_circ{'usr'};
-        my $last_circ_from_prev_chain = $e->json_query({
-            'select' => { 'circ' => ['id','usr'] },
-            'from' => 'circ', 
-            'where' => {
-                target_copy => $target_copy,
-                xact_start => { '<' => $$first_circ{'xact_start'} }
+    my $first_circ = 
+        $e->json_query({from => ['action.all_circ_chain', $circ_id]})->[0];
+
+    my $prev_circ = $e->search_action_all_circulation_slim([
+        {   target_copy => $first_circ->{target_copy},
+            xact_start => {'<' => $first_circ->{xact_start}}
+        }, {   
+            flesh => 1,
+            flesh_fields => {
+                aacs => [
+                    'active_circ',
+                    'aged_circ'
+                ]
             },
-            'order_by' => [{ 'class'=>'circ', 'field'=>'xact_start', 'direction'=>'desc' }],
-            'limit' => 1
-        })->[0];
-        return undef unless $last_circ_from_prev_chain;
-        return undef unless $$last_circ_from_prev_chain{'id'};
-        my $sum = $e->json_query({from => ['action.summarize_circ_chain', $$last_circ_from_prev_chain{'id'}]})->[0];
-        return undef unless $sum;
-        my $obj = Fieldmapper::action::circ_chain_summary->new;
-        $obj->$_($sum->{$_}) for keys %$sum;
-        return { 'summary' => $obj, 'usr' => $$last_circ_from_prev_chain{'usr'} };
+            order_by => { aacs => 'xact_start desc' },
+            limit => 1 
+        }
+    ])->[0];
 
-    } else {
+    return undef unless $prev_circ;
 
-        my $first_circ = $e->json_query({from => ['action.circ_chain', $circ_id]})->[0];
-        my $target_copy = $$first_circ{'target_copy'};
-        my $last_circ_from_prev_chain = $e->json_query({
-            'select' => { 'circ' => ['id'] },
-            'from' => 'circ', 
-            'where' => {
-                target_copy => $target_copy,
-                xact_start => { '<' => $$first_circ{'xact_start'} }
-            },
-            'order_by' => [{ 'class'=>'circ', 'field'=>'xact_start', 'direction'=>'desc' }],
-            'limit' => 1
+    my $chain_usr = $prev_circ->usr; # note: may be undef
+
+    if ($self->api_name =~ /summary/) {
+        my $sum = $e->json_query({
+            from => [
+                'action.summarize_all_circ_chain', 
+                $prev_circ->id
+            ]
         })->[0];
-        return undef unless $last_circ_from_prev_chain;
-        return undef unless $$last_circ_from_prev_chain{'id'};
-        my $chain = $e->json_query({from => ['action.circ_chain', $$last_circ_from_prev_chain{'id'}]});
 
-        for my $circ_info (@$chain) {
-            my $circ = Fieldmapper::action::circulation->new;
-            $circ->$_($circ_info->{$_}) for keys %$circ_info;
-            $conn->respond($circ);
-        }
+        my $summary = Fieldmapper::action::circ_chain_summary->new;
+        $summary->$_($sum->{$_}) for keys %$sum;
+
+        return {summary => $summary, usr => $chain_usr};
+    }
+
+
+    my $chain = $e->json_query(
+        {from => ['action.all_circ_chain', $prev_circ->id]});
+
+    for my $circ_info (@$chain) {
+        my $circ = Fieldmapper::action::all_circulation_slim->new;
+        $circ->$_($circ_info->{$_}) for keys %$circ_info;
+        $conn->respond($circ);
     }
 
     return undef;
@@ -1816,9 +2096,9 @@ sub retrieve_prev_circ_chain {
 
 
 __PACKAGE__->register_method(
-       method  => "get_copy_due_date",
-       api_name        => "open-ils.circ.copy.due_date.retrieve",
-       signature => {
+    method  => "get_copy_due_date",
+    api_name    => "open-ils.circ.copy.due_date.retrieve",
+    signature => {
         desc => q/
             Given a copy ID, returns the due date for the copy if it's 
             currently circulating.  Otherwise, returns null.  Note, this is a public