From 2a48dc79de8e52c694dc9a1fec4f23f3aed125f0 Mon Sep 17 00:00:00 2001
From: Kyle Hall <kyle@bywatersolutions.com>
Date: Tue, 15 Apr 2025 16:32:40 -0300
Subject: [PATCH] Bug 32034: Add item transfers to action logs

Having branch transfers in the action logs would help to more fully show the cycle of holds that require transfers.

Test Plan:
1) Apply this patch
2) Run updatedatabase.pl
3) Restart all the things!
4) Enable the new syspref LogTransfers
5) Create and finish some transfers, note the new logs in the log
   viewer!

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
---
 Koha/Item/Transfer.pm                         | 34 ++++++++++++++++++-
 .../prog/en/includes/action-logs.inc          |  2 ++
 .../prog/en/modules/tools/viewlog.tt          |  2 +-
 t/db_dependent/Koha/Item.t                    | 31 +++++++++++++++--
 4 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/Koha/Item/Transfer.pm b/Koha/Item/Transfer.pm
index e7fe4897a24..1cacb684754 100644
--- a/Koha/Item/Transfer.pm
+++ b/Koha/Item/Transfer.pm
@@ -17,7 +17,10 @@ package Koha::Item::Transfer;
 
 use Modern::Perl;
 
+use JSON qw(to_json);
+
 use C4::Items qw( CartToShelf ModDateLastSeen );
+use C4::Log   qw( logaction );
 
 use Koha::Database;
 use Koha::DateUtils qw( dt_from_string );
@@ -31,10 +34,39 @@ Koha::Item::Transfer - Koha Item Transfer Object class
 
 =head1 API
 
-=head2 Class Methods
+=head2 Class methods
+
+=head3 store
+
+Overloaded I<store> method.
 
 =cut
 
+sub store {
+    my ($self) = @_;
+
+    $self->_result->result_source->schema->txn_do(
+        sub {
+            my $action = $self->in_storage ? 'UPDATE' : 'CREATE';
+
+            $self = $self->SUPER::store;
+            $self->discard_changes;
+
+            logaction(
+                "TRANSFERS",
+                $action,
+                $self->itemnumber,
+                to_json(
+                    $self->TO_JSON,
+                    { utf8 => 1, pretty => 1, canonical => 1, }
+                )
+            ) if C4::Context->preference("TransfersLog");
+        }
+    );
+
+    return $self;
+}
+
 =head3 item
 
   my $item = $transfer->item;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/action-logs.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/action-logs.inc
index 73d5a819e63..329a721c9fa 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/action-logs.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/action-logs.inc
@@ -40,6 +40,8 @@
         <span>Recalls</span>[% UNLESS Koha.Preference('RecallsLog') %]<i class="log-disabled fa-solid fa-triangle-exclamation" title="Log not enabled" data-log="RecallsLog"></i>[% END %]
     [% CASE 'SUGGESTION' %]
         <span>Suggestions</span>[% UNLESS Koha.Preference('SuggestionsLog') %]<i class="log-disabled fa-solid fa-triangle-exclamation" title="Log not enabled" data-log="SuggestionsLog"></i>[% END %]
+    [% CASE 'TRANSFERS' %]
+        <span>Transfers</span>[% UNLESS Koha.Preference('TransfersLog') %]<i class="log-disabled fa-solid fa-triangle-exclamation" title="Log not enabled" data-log="TransfersLog"></i>[% END %]
     [% CASE %]
         [% module | html %]
     [% END %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt
index 23b9c7b8764..cd6f64b2a3e 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt
@@ -123,7 +123,7 @@
                             [% ELSE %]
                                 <label for="moduleALL" class="viewlog"><input type="checkbox" id="moduleALL" name="modules" value="" /> All</label>
                             [% END %]
-                            [% FOREACH modx IN [ 'AUTH' 'CATALOGUING' 'AUTHORITIES' 'MEMBERS' 'ACQUISITIONS' 'SERIAL' 'HOLDS' 'ILL' 'CIRCULATION' 'CLAIMS' 'FINES' 'SYSTEMPREFERENCE' 'CRONJOBS', 'REPORTS', 'SEARCHENGINE', 'NOTICES', 'NEWS', 'RECALLS', 'SUGGESTION' ] %]
+                            [% FOREACH modx IN [ 'AUTH' 'CATALOGUING' 'AUTHORITIES' 'MEMBERS' 'ACQUISITIONS' 'SERIAL' 'HOLDS' 'ILL' 'CIRCULATION' 'CLAIMS' 'FINES' 'SYSTEMPREFERENCE' 'CRONJOBS', 'REPORTS', 'SEARCHENGINE', 'NOTICES', 'NEWS', 'RECALLS', 'SUGGESTION', 'TRANSFERS' ] %]
                                 [% IF modules.grep(modx).size %]
                                     <label for="module[% modx | html %]" class="viewlog"
                                         ><input type="checkbox" id="module[% modx | html %]" name="modules" value="[% modx | html %]" checked="checked" /> [% PROCESS translate_log_module module=modx %]</label
diff --git a/t/db_dependent/Koha/Item.t b/t/db_dependent/Koha/Item.t
index 06fe7af209b..6c81bad0451 100755
--- a/t/db_dependent/Koha/Item.t
+++ b/t/db_dependent/Koha/Item.t
@@ -887,8 +887,10 @@ subtest 'pickup_locations() tests' => sub {
     $schema->storage->txn_rollback;
 };
 
-subtest 'request_transfer' => sub {
-    plan tests => 14;
+subtest 'request_transfer() tests' => sub {
+
+    plan tests => 18;
+
     $schema->storage->txn_begin;
 
     my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
@@ -910,11 +912,36 @@ subtest 'request_transfer' => sub {
         'Exception thrown if `to` parameter is missing';
 
     # Successful request
+    t::lib::Mocks::mock_preference( 'TransfersLog', 0 );
+
+    $schema->resultset('ActionLog')->search()->delete();
     my $transfer = $item->request_transfer( { to => $library1, reason => 'Manual' } );
     is(
         ref($transfer), 'Koha::Item::Transfer',
         'Koha::Item->request_transfer should return a Koha::Item::Transfer object'
     );
+    is( $schema->resultset('ActionLog')->count(), 0, 'False value for TransfersLog does not trigger logging' );
+    $transfer->delete;
+
+    t::lib::Mocks::mock_preference( 'TransfersLog', 1 );
+
+    $transfer = $item->request_transfer( { to => $library1, reason => 'Manual' } );
+    is(
+        ref($transfer), 'Koha::Item::Transfer',
+        'Koha::Item->request_transfer should return a Koha::Item::Transfer object'
+    );
+    is( $schema->resultset('ActionLog')->count(), 1, 'True value for TransfersLog does trigger logging' );
+    $transfer->delete;
+
+    t::lib::Mocks::mock_preference( 'TransfersLog', 0 );
+
+    # Successful request
+    $transfer = $item->request_transfer( { to => $library1, reason => 'Manual' } );
+    is(
+        ref($transfer), 'Koha::Item::Transfer',
+        'Koha::Item->request_transfer should return a Koha::Item::Transfer object'
+    );
+
     my $original_transfer = $transfer->get_from_storage;
 
     # Transfer already in progress
-- 
2.49.0