LP 1779467: Fix Error When Marking Item on Hold as Discard/Weed
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Application / Circ.pm
index ddddbc8..0dfebd3 100644 (file)
@@ -20,7 +20,7 @@ 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;
@@ -371,10 +371,10 @@ sub new_set_circ_lost {
 }
 
 __PACKAGE__->register_method(
-    method    => "update_last_copy_inventory",
-    api_name  => "open-ils.circ.circulation.update_last_copy_inventory");
+    method    => "update_latest_inventory",
+    api_name  => "open-ils.circ.circulation.update_latest_inventory");
 
-sub update_last_copy_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;
@@ -382,21 +382,21 @@ sub update_last_copy_inventory {
     my $copies = $$args{copy_list};
     foreach my $copyid (@$copies) {
         my $copy = $e->retrieve_asset_copy($copyid);
-        my $alci = $e->search_asset_last_copy_inventory({copy => $copyid})->[0];
+        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_last_copy_inventory($alci) or return $e->die_event;
+            $e->update_asset_latest_inventory($alci) or return $e->die_event;
         } else {
-            my $alci = Fieldmapper::asset::last_copy_inventory->new;
+            my $alci = Fieldmapper::asset::latest_inventory->new;
             $alci->inventory_date('now');
             $alci->inventory_workstation($e->requestor->wsid);
             $alci->copy($copy->id);
-            $e->create_asset_last_copy_inventory($alci) or return $e->die_event;
+            $e->create_asset_latest_inventory($alci) or return $e->die_event;
         }
 
-        $copy->last_copy_inventory($alci);
+        $copy->latest_inventory($alci);
     }
     $e->commit;
     return 1;
@@ -488,14 +488,14 @@ sub set_circ_claims_returned {
     $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);
@@ -642,8 +642,8 @@ 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
@@ -680,12 +680,17 @@ 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);
@@ -743,7 +748,7 @@ sub create_in_house_use {
     }
 
     if( $use_time ne 'now' ) {
-        $use_time = cleanse_ISO8601($use_time);
+        $use_time = clean_ISO8601($use_time);
         $logger->debug("in_house_use setting use time to $use_time");
     }
 
@@ -1302,31 +1307,24 @@ sub mark_item {
     my( $self, $conn, $auth, $copy_id, $args ) = @_;
     $args ||= {};
 
-    # Items must be checked in before any attempt is made to mark damaged
-    my $evt = try_checkin($auth, $copy_id) if
-        ($self->api_name=~ /damaged/ && $args->{handle_checkin});
-    return $evt if $evt;
-
-    my $e = new_editor(authtoken=>$auth, xact =>1);
+    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 $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;
-        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;
@@ -1350,19 +1348,72 @@ sub mark_item {
     # 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);
-
-    $e->update_asset_copy($copy) or return $e->die_event;
+    # 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;
 
-    my $holds = $e->search_action_hold_request(
-        { 
+    # 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;
+    }
+
+    # Mark the copy.
+    $copy->status($stat);
+    $copy->edit_date('now');
+    $copy->editor($e->requestor->id);
+
+    $e->update_asset_copy($copy) or return $e->die_event;
 
     $e->commit;
 
@@ -1403,6 +1454,19 @@ sub try_checkin {
     }
 }
 
+sub try_abort_transit {
+    my ($auth, $copy_id) = @_;
+
+    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 {
     my($e, $copy, $owning_lib, $args) = @_;
 
@@ -1590,7 +1654,7 @@ sub mark_item_missing_pieces {
 
                 $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') );
+                $co_params->{'due_date'} = clean_ISO8601( $due_date->strftime('%FT%T%z') );
             } else {
                 $logger->info('open-ils.circ.mark_item_missing_pieces: item not needed for hold');
             }