Bug 23463: Fix CancelReceipt.t
[srvgit] / Koha / Item.pm
index d2b1b9d..4304b8a 100644 (file)
@@ -21,6 +21,8 @@ use Modern::Perl;
 
 use Carp;
 use List::MoreUtils qw(any);
+use Data::Dumper;
+use Try::Tiny;
 
 use Koha::Database;
 use Koha::DateUtils qw( dt_from_string );
@@ -28,11 +30,16 @@ use Koha::DateUtils qw( dt_from_string );
 use C4::Context;
 use C4::Circulation;
 use C4::Reserves;
+use C4::Biblio qw( ModZebra ); # FIXME This is terrible, we should move the indexation code outside of C4::Biblio
+use C4::ClassSource; # FIXME We would like to avoid that
+use C4::Log qw( logaction );
+
 use Koha::Checkouts;
 use Koha::CirculationRules;
 use Koha::Item::Transfer::Limits;
 use Koha::Item::Transfers;
 use Koha::Patrons;
+use Koha::Plugins;
 use Koha::Libraries;
 use Koha::StockRotationItem;
 use Koha::StockRotationRotas;
@@ -49,6 +56,198 @@ Koha::Item - Koha Item object class
 
 =cut
 
+=head3 store
+
+=cut
+
+sub store {
+    my ($self, $params) = @_;
+
+    my $log_action = $params->{log_action} // 1;
+
+    # We do not want to oblige callers to pass this value
+    # Dev conveniences vs performance?
+    unless ( $self->biblioitemnumber ) {
+        $self->biblioitemnumber( $self->biblio->biblioitem->biblioitemnumber );
+    }
+
+    # See related changes from C4::Items::AddItem
+    unless ( $self->itype ) {
+        $self->itype($self->biblio->biblioitem->itemtype);
+    }
+
+    if ( $self->itemcallnumber ) { # This could be improved, we should recalculate it only if changed
+        my $cn_sort = GetClassSort($self->cn_source, $self->itemcallnumber, "");
+        $self->cn_sort($cn_sort);
+    }
+
+    my $today = dt_from_string;
+    unless ( $self->in_storage ) { #AddItem
+        unless ( $self->permanent_location ) {
+            $self->permanent_location($self->location);
+        }
+        unless ( $self->replacementpricedate ) {
+            $self->replacementpricedate($today);
+        }
+        unless ( $self->datelastseen ) {
+            $self->datelastseen($today);
+        }
+
+        unless ( $self->dateaccessioned ) {
+            $self->dateaccessioned($today);
+        }
+
+        C4::Biblio::ModZebra( $self->biblionumber, "specialUpdate", "biblioserver" );
+
+        logaction( "CATALOGUING", "ADD", $self->itemnumber, "item" )
+          if $log_action && C4::Context->preference("CataloguingLog");
+
+        $self->_after_item_action_hooks({ action => 'create' });
+
+    } else { # ModItem
+
+        { # Update *_on  fields if needed
+          # Why not for AddItem as well?
+            my @fields = qw( itemlost withdrawn damaged );
+
+            # Only retrieve the item if we need to set an "on" date field
+            if ( $self->itemlost || $self->withdrawn || $self->damaged ) {
+                my $pre_mod_item = $self->get_from_storage;
+                for my $field (@fields) {
+                    if (    $self->$field
+                        and not $pre_mod_item->$field )
+                    {
+                        my $field_on = "${field}_on";
+                        $self->$field_on(
+                          DateTime::Format::MySQL->format_datetime( dt_from_string() )
+                        );
+                    }
+                }
+            }
+
+            # If the field is defined but empty, we are removing and,
+            # and thus need to clear out the 'on' field as well
+            for my $field (@fields) {
+                if ( defined( $self->$field ) && !$self->$field ) {
+                    my $field_on = "${field}_on";
+                    $self->$field_on(undef);
+                }
+            }
+        }
+
+        my %updated_columns = $self->_result->get_dirty_columns;
+        if (    defined $self->location
+            and $self->location ne 'CART'
+            and $self->location ne 'PROC'
+            and not exists $updated_columns{permanent_location} )
+        {
+            $self->permanent_location( $self->location );
+        }
+
+        $self->timestamp(undef) if $self->timestamp; # Maybe move this to Koha::Object->store?
+
+        C4::Biblio::ModZebra( $self->biblionumber, "specialUpdate", "biblioserver" );
+
+        $self->_after_item_action_hooks({ action => 'modify' });
+
+        logaction( "CATALOGUING", "MODIFY", $self->itemnumber, "item " . Dumper($self->unblessed) )
+          if $log_action && C4::Context->preference("CataloguingLog");
+    }
+
+    unless ( $self->dateaccessioned ) {
+        $self->dateaccessioned($today);
+    }
+
+    return $self->SUPER::store;
+}
+
+=head3 delete
+
+=cut
+
+sub delete {
+    my ( $self ) = @_;
+
+    # FIXME check the item has no current issues
+    # i.e. raise the appropriate exception
+
+    C4::Biblio::ModZebra( $self->biblionumber, "specialUpdate", "biblioserver" );
+
+    $self->_after_item_action_hooks({ action => 'delete' });
+
+    logaction( "CATALOGUING", "DELETE", $self->itemnumber, "item" )
+      if C4::Context->preference("CataloguingLog");
+
+    return $self->SUPER::delete;
+}
+
+=head3 safe_delete
+
+=cut
+
+sub safe_delete {
+    my ($self) = @_;
+
+    my $safe_to_delete = $self->safe_to_delete;
+    return $safe_to_delete unless $safe_to_delete eq '1';
+
+    $self->move_to_deleted;
+
+    return $self->delete;
+}
+
+=head3 safe_to_delete
+
+returns 1 if the item is safe to delete,
+
+"book_on_loan" if the item is checked out,
+
+"not_same_branch" if the item is blocked by independent branches,
+
+"book_reserved" if the there are holds aganst the item, or
+
+"linked_analytics" if the item has linked analytic records.
+
+=cut
+
+sub safe_to_delete {
+    my ($self) = @_;
+
+    return "book_on_loan" if $self->checkout;
+
+    return "not_same_branch"
+      if defined C4::Context->userenv
+      and !C4::Context->IsSuperLibrarian()
+      and C4::Context->preference("IndependentBranches")
+      and ( C4::Context->userenv->{branch} ne $self->homebranch );
+
+    # check it doesn't have a waiting reserve
+    return "book_reserved"
+      if $self->holds->search( { found => [ 'W', 'T' ] } )->count;
+
+    return "linked_analytics"
+      if C4::Items::GetAnalyticsCount( $self->itemnumber ) > 0;
+
+    return 1;
+}
+
+=head3 move_to_deleted
+
+my $is_moved = $item->move_to_deleted;
+
+Move an item to the deleteditems table.
+This can be done before deleting an item, to make sure the data are not completely deleted.
+
+=cut
+
+sub move_to_deleted {
+    my ($self) = @_;
+    my $item_infos = $self->unblessed;
+    delete $item_infos->{timestamp}; #This ensures the timestamp date in deleteditems will be set to the current timestamp
+    return Koha::Database->new->schema->resultset('Deleteditem')->create($item_infos);
+}
+
+
 =head3 effective_itemtype
 
 Returns the itemtype for the item based on whether item level itemtypes are set or not.
@@ -402,6 +601,16 @@ sub current_holds {
     return Koha::Holds->_new_from_dbic($hold_rs);
 }
 
+=head3 holds
+
+=cut
+
+sub holds {
+    my ( $self ) = @_;
+    my $hold_rs = $self->_result->reserves->search;
+    return Koha::Holds->_new_from_dbic($hold_rs);
+}
+
 =head3 stockrotationitem
 
   my $sritem = Koha::Item->stockrotationitem;
@@ -549,6 +758,37 @@ sub to_api_mapping {
 
 =head2 Internal methods
 
+=head3 _after_item_action_hooks
+
+Helper method that takes care of calling all plugin hooks
+
+=cut
+
+sub _after_item_action_hooks {
+    my ( $self, $params ) = @_;
+
+    my $action = $params->{action};
+
+    if ( C4::Context->preference('UseKohaPlugins') && C4::Context->config("enable_plugins") ) {
+
+        my @plugins = Koha::Plugins->new->GetPlugins({
+            method => 'after_item_action',
+        });
+
+        if (@plugins) {
+
+            foreach my $plugin ( @plugins ) {
+                try {
+                    $plugin->after_item_action({ action => $action, item => $self, item_id => $self->itemnumber });
+                }
+                catch {
+                    warn "$_";
+                };
+            }
+        }
+    }
+}
+
 =head3 _type
 
 =cut