Bug 23463: Fix CancelReceipt.t
[srvgit] / Koha / Item.pm
index 3cab6d2..4304b8a 100644 (file)
@@ -4,34 +4,42 @@ package Koha::Item;
 #
 # This file is part of Koha.
 #
-# Koha is free software; you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 3 of the License, or (at your option) any later
-# version.
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
 #
-# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
 #
-# You should have received a copy of the GNU General Public License along
-# with Koha; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 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 );
 
 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::IssuingRules;
+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;
@@ -48,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.
@@ -299,7 +499,7 @@ sub can_be_transferred {
 @pickup_locations = $item->pickup_locations( {patron => $patron } )
 
 Returns possible pickup locations for this item, according to patron's home library (if patron is defined and holds are allowed only from hold groups)
-and if item can be transfered to each pickup location.
+and if item can be transferred to each pickup location.
 
 =cut
 
@@ -309,23 +509,23 @@ sub pickup_locations {
     my $patron = $params->{patron};
 
     my $circ_control_branch =
-      C4::Circulation::_GetCircControlBranch( $self->unblessed(), $patron );
+      C4::Reserves::GetReservesControlBranch( $self->unblessed(), $patron->unblessed );
     my $branchitemrule =
       C4::Circulation::GetBranchItemRule( $circ_control_branch, $self->itype );
 
-    my $branch_control = C4::Context->preference('HomeOrHoldingBranch');
-    my $library = $branch_control eq 'holdingbranch' ? $self->holding_branch : $self->home_branch;
-
     my @libs;
     if(defined $patron) {
-        return @libs if $branchitemrule->{holdallowed} == 3 && !$library->validate_hold_sibling( {branchcode => $patron->branchcode} );
-        return @libs if $branchitemrule->{holdallowed} == 1 && $library->branchcode ne $patron->branchcode;
+        return @libs if $branchitemrule->{holdallowed} == 3 && !$self->home_branch->validate_hold_sibling( {branchcode => $patron->branchcode} );
+        return @libs if $branchitemrule->{holdallowed} == 1 && $self->home_branch->branchcode ne $patron->branchcode;
     }
 
     if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup') {
-        @libs  = $library->get_hold_libraries;
-        my $circ_control_library = Koha::Libraries->find($circ_control_branch);
-        push @libs, $circ_control_library unless scalar(@libs) > 0;
+        @libs  = $self->home_branch->get_hold_libraries;
+        push @libs, $self->home_branch unless scalar(@libs) > 0;
+    } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup') {
+        my $plib = Koha::Libraries->find({ branchcode => $patron->branchcode});
+        @libs  = $plib->get_hold_libraries;
+        push @libs, $self->home_branch unless scalar(@libs) > 0;
     } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'homebranch') {
         push @libs, $self->home_branch;
     } elsif ($branchitemrule->{hold_fulfillment_policy} eq 'holdingbranch') {
@@ -341,9 +541,10 @@ sub pickup_locations {
     my @pickup_locations;
     foreach my $library (@libs) {
         if ($library->pickup_location && $self->can_be_transferred({ to => $library })) {
-            push @pickup_locations, $library->unblessed;
+            push @pickup_locations, $library;
         }
     }
+
     return wantarray ? @pickup_locations : \@pickup_locations;
 }
 
@@ -367,10 +568,17 @@ sub article_request_type {
       :                                      undef;
     my $borrowertype = $borrower->categorycode;
     my $itemtype = $self->effective_itemtype();
-    my $issuing_rule = Koha::IssuingRules->get_effective_issuing_rule({ categorycode => $borrowertype, itemtype => $itemtype, branchcode => $branchcode });
+    my $rule = Koha::CirculationRules->get_effective_rule(
+        {
+            rule_name    => 'article_requests',
+            categorycode => $borrowertype,
+            itemtype     => $itemtype,
+            branchcode   => $branchcode
+        }
+    );
 
-    return q{} unless $issuing_rule;
-    return $issuing_rule->article_requests || q{}
+    return q{} unless $rule;
+    return $rule->rule_value || q{}
 }
 
 =head3 current_holds
@@ -393,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;
@@ -540,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