From 3f6f53ad1c1c67b3d07d9bade7bf3c39dcdb8866 Mon Sep 17 00:00:00 2001 From: Stefan Berndtsson Date: Tue, 4 Oct 2022 10:54:43 +0200 Subject: [PATCH] Bug 31734: Extend hooks for plugins Hooks added: after_account_action / add in Koha::Account::add_credit when adding credit for payment or writeoff. after_hold_action / transfer, waiting and processing in Koha::Hold::set_* after_recall_action / add in Koha::Recalls::add_recall when adding recall before_index_action / update in Koha::SearchEngine::Elasticsearch::Indexer::update_index before actually updating the index Tests added for all new hooks. How to test: 1) Run tests in t/db_dependent/Koha/Plugins/Account_hooks.t 2) Run tests in t/db_dependent/Koha/Plugins/Elasticsearch_Indexer_hooks.t 3) Run tests in t/db_dependent/Koha/Plugins/Holds_hooks.t 4) Run tests in t/db_dependent/Koha/Plugins/Recall_hooks.t Sponsored by: Gothenburg University Library --- Koha/Account.pm | 12 +++ Koha/Hold.pm | 24 +++++ Koha/Recalls.pm | 9 ++ Koha/SearchEngine/Elasticsearch/Indexer.pm | 9 ++ t/db_dependent/Koha/Plugins/Account_hooks.t | 74 ++++++++++++++ .../Plugins/Elasticsearch_Indexer_hooks.t | 83 ++++++++++++++++ t/db_dependent/Koha/Plugins/Holds_hooks.t | 20 +++- t/db_dependent/Koha/Plugins/Recall_hooks.t | 98 +++++++++++++++++++ t/lib/plugins/Koha/Plugin/Test.pm | 32 ++++++ 9 files changed, 360 insertions(+), 1 deletion(-) create mode 100755 t/db_dependent/Koha/Plugins/Account_hooks.t create mode 100644 t/db_dependent/Koha/Plugins/Elasticsearch_Indexer_hooks.t create mode 100644 t/db_dependent/Koha/Plugins/Recall_hooks.t diff --git a/Koha/Account.pm b/Koha/Account.pm index 4c13a9a07e..cdbf12cc39 100644 --- a/Koha/Account.pm +++ b/Koha/Account.pm @@ -35,6 +35,7 @@ use Koha::Account::Offsets; use Koha::Account::DebitTypes; use Koha::Exceptions; use Koha::Exceptions::Account; +use Koha::Plugins; =head1 NAME @@ -255,6 +256,17 @@ sub add_credit { } ) if grep { $credit_type eq $_ } ( 'PAYMENT', 'WRITEOFF' ); + Koha::Plugins->call( + 'after_account_action', + { + action => "add_credit", + payload => { + type => lc($credit_type), + line => $line->get_from_storage + } + } + ); + if ( C4::Context->preference("FinesLog") ) { logaction( "FINES", 'CREATE', diff --git a/Koha/Hold.pm b/Koha/Hold.pm index ca261086c7..9738417f75 100644 --- a/Koha/Hold.pm +++ b/Koha/Hold.pm @@ -200,6 +200,14 @@ sub set_transfer { $self->found('T'); $self->store(); + Koha::Plugins->call( + 'after_hold_action', + { + action => 'transfer', + payload => { hold => $self->get_from_storage } + } + ); + return $self; } @@ -257,6 +265,14 @@ sub set_waiting { $self->set($values)->store(); + Koha::Plugins->call( + 'after_hold_action', + { + action => 'waiting', + payload => { hold => $self->get_from_storage } + } + ); + return $self; } @@ -345,6 +361,14 @@ sub set_processing { $self->found('P'); $self->store(); + Koha::Plugins->call( + 'after_hold_action', + { + action => 'processing', + payload => { hold => $self->get_from_storage } + } + ); + return $self; } diff --git a/Koha/Recalls.pm b/Koha/Recalls.pm index ef73bf9917..342f808d93 100644 --- a/Koha/Recalls.pm +++ b/Koha/Recalls.pm @@ -22,6 +22,7 @@ use Modern::Perl; use Koha::Database; use Koha::Recall; use Koha::DateUtils qw( dt_from_string ); +use Koha::Plugins; use C4::Stats qw( UpdateStats ); @@ -173,6 +174,14 @@ sub add_recall { ccode => $item->ccode, }); + Koha::Plugins->call( + 'after_recall_action', + { + action => 'add', + payload => { recall => $recall } + } + ); + # add action log C4::Log::logaction( 'RECALLS', 'CREATE', $recall->id, "Recall requested by borrower #" . $recall->patron_id, $interface ) if ( C4::Context->preference('RecallsLog') ); diff --git a/Koha/SearchEngine/Elasticsearch/Indexer.pm b/Koha/SearchEngine/Elasticsearch/Indexer.pm index 6101a3a372..09a5c13c88 100644 --- a/Koha/SearchEngine/Elasticsearch/Indexer.pm +++ b/Koha/SearchEngine/Elasticsearch/Indexer.pm @@ -116,6 +116,15 @@ sub update_index { $index_record_ids = $record_ids; } + Koha::Plugins->call('before_index_action', { + action => 'update', + payload => { + engine => 'Elasticsearch', + records => $records, + record_ids => $index_record_ids + } + }); + my $documents = $self->marc_records_to_documents($records); my @body; for (my $i = 0; $i < scalar @$index_record_ids; $i++) { diff --git a/t/db_dependent/Koha/Plugins/Account_hooks.t b/t/db_dependent/Koha/Plugins/Account_hooks.t new file mode 100755 index 0000000000..8cfbec623b --- /dev/null +++ b/t/db_dependent/Koha/Plugins/Account_hooks.t @@ -0,0 +1,74 @@ +#!/usr/bin/perl + +# 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 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, see . + +use Modern::Perl; + +use Test::More tests => 4; +use Test::MockModule; +use Test::Warn; + +use File::Basename; + +use Koha::Account qw( add_credit ); + +use t::lib::Mocks; +use t::lib::TestBuilder; + +BEGIN { + # Mock pluginsdir before loading Plugins module + my $path = dirname(__FILE__) . '/../../../lib/plugins'; + t::lib::Mocks::mock_config( 'pluginsdir', $path ); + + use_ok('Koha::Plugins'); + use_ok('Koha::Plugins::Handler'); + use_ok('Koha::Plugin::Test'); +} + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +t::lib::Mocks::mock_config( 'enable_plugins', 1 ); + +subtest 'Koha::Account tests' => sub { + + plan tests => 2; + + $schema->storage->txn_begin; + + my $plugins = Koha::Plugins->new; + $plugins->InstallPlugins; + + my $plugin = Koha::Plugin::Test->new->enable; + + my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); + + my $account = $patron->account; + warning_like { + $account->add_credit({ amount => 20, interface => 'commandline', type => 'WRITEOFF'}); + } + qr/after_account_action called with action: add_credit, type: writeoff, ref: Koha::Account::Line/, + '->add_credit calls the after_account_action hook with type writeoff'; + + + warning_like { + $account->add_credit({ amount => 10, interface => 'commandline', type => 'PAYMENT'}); + } + qr/after_account_action called with action: add_credit, type: payment, ref: Koha::Account::Line/, + '->add_credit calls the after_account_action hook with type payment'; + + $schema->storage->txn_rollback; + Koha::Plugins::Methods->delete; +}; diff --git a/t/db_dependent/Koha/Plugins/Elasticsearch_Indexer_hooks.t b/t/db_dependent/Koha/Plugins/Elasticsearch_Indexer_hooks.t new file mode 100644 index 0000000000..5639042fdb --- /dev/null +++ b/t/db_dependent/Koha/Plugins/Elasticsearch_Indexer_hooks.t @@ -0,0 +1,83 @@ +#!/usr/bin/perl + +# 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 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, see . + +use Modern::Perl; + +use Test::More tests => 5; +use Test::MockModule; +use Test::Warn; +use t::lib::Mocks; +use t::lib::TestBuilder; + +use File::Basename; + +use MARC::Record; + +use Koha::Database; +use Koha::Biblios; + +BEGIN { + # Mock pluginsdir before loading Plugins module + my $path = dirname(__FILE__) . '/../../../lib/plugins'; + t::lib::Mocks::mock_config( 'pluginsdir', $path ); + + use_ok('Koha::Plugins'); + use_ok('Koha::Plugins::Handler'); + use_ok('Koha::Plugin::Test'); +} + +my $schema = Koha::Database->schema(); + +use_ok('Koha::SearchEngine::Elasticsearch::Indexer'); + +SKIP: { + + eval { Koha::SearchEngine::Elasticsearch->get_elasticsearch_params; }; + + skip 'Elasticsearch configuration not available', 2 + if $@; + +my $builder = t::lib::TestBuilder->new; +my $biblio = $builder->build_sample_biblio; # create biblio before we start mocking to avoid trouble indexing on creation +t::lib::Mocks::mock_config( 'enable_plugins', 1 ); + +subtest 'before_index_action hook' => sub { + plan tests => 1; + + $schema->storage->txn_begin; + my $plugins = Koha::Plugins->new; + $plugins->InstallPlugins; + + my $plugin = Koha::Plugin::Test->new->enable; + my $test_plugin = Test::MockModule->new('Koha::Plugin::Test'); + $test_plugin->mock( 'after_biblio_action', undef ); + + my $biblio = $builder->build_sample_biblio; + my $biblionumber = $biblio->biblionumber; + + my $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new({ 'index' => 'biblios' }); + warning_like { + $indexer->update_index([$biblionumber]); + } + qr/before_index_action called with action: update, engine: Elasticsearch, ref: ARRAY/, + '-> update_index calls the before_index_action hook'; + + $schema->storage->txn_rollback; + Koha::Plugins::Methods->delete; +}; + +} diff --git a/t/db_dependent/Koha/Plugins/Holds_hooks.t b/t/db_dependent/Koha/Plugins/Holds_hooks.t index c259aeb1bc..d1a9dba00d 100755 --- a/t/db_dependent/Koha/Plugins/Holds_hooks.t +++ b/t/db_dependent/Koha/Plugins/Holds_hooks.t @@ -130,7 +130,7 @@ subtest 'after_hold_action (placed) hook tests' => sub { subtest 'Koha::Hold tests' => sub { - plan tests => 4; + plan tests => 7; $schema->storage->txn_begin; @@ -181,6 +181,24 @@ subtest 'Koha::Hold tests' => sub { qr/after_hold_action called with action: resume, ref: Koha::Hold/, '->resume calls the after_hold_action hook'; + warning_like { + $hold->set_transfer; + } + qr/after_hold_action called with action: transfer, ref: Koha::Hold/, + '->set_transfer calls the after_hold_action hook'; + + warning_like { + $hold->set_processing; + } + qr/after_hold_action called with action: processing, ref: Koha::Hold/, + '->set_processing calls the after_hold_action hook'; + + warning_like { + $hold->set_waiting; + } + qr/after_hold_action called with action: waiting, ref: Koha::Hold/, + '->set_waiting calls the after_hold_action hook'; + warning_like { $hold->cancel; } diff --git a/t/db_dependent/Koha/Plugins/Recall_hooks.t b/t/db_dependent/Koha/Plugins/Recall_hooks.t new file mode 100644 index 0000000000..c46d7dcab1 --- /dev/null +++ b/t/db_dependent/Koha/Plugins/Recall_hooks.t @@ -0,0 +1,98 @@ +#!/usr/bin/perl + +# 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 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, see . + +use Modern::Perl; + +use Test::More tests => 4; +use Test::MockModule; +use Test::Warn; + +use File::Basename; + +use Koha::Account qw( add_credit ); + +use t::lib::Mocks; +use t::lib::TestBuilder; + +BEGIN { + # Mock pluginsdir before loading Plugins module + my $path = dirname(__FILE__) . '/../../../lib/plugins'; + t::lib::Mocks::mock_config( 'pluginsdir', $path ); + + use_ok('Koha::Plugins'); + use_ok('Koha::Plugins::Handler'); + use_ok('Koha::Plugin::Test'); +} + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +t::lib::Mocks::mock_config( 'enable_plugins', 1 ); + +subtest 'after_recall_action hook' => sub { + + plan tests => 1; + + $schema->storage->txn_begin; + + my $plugins = Koha::Plugins->new; + $plugins->InstallPlugins; + + my $plugin = Koha::Plugin::Test->new->enable; + # Avoid testing useless warnings + my $test_plugin = Test::MockModule->new('Koha::Plugin::Test'); + $test_plugin->mock( 'after_item_action', undef ); + $test_plugin->mock( 'after_circ_action', undef ); + $test_plugin->mock( 'after_biblio_action', undef ); + $test_plugin->mock( 'patron_barcode_transform', undef ); + $test_plugin->mock( 'item_barcode_transform', undef ); + + my $item = $builder->build_sample_item(); + my $biblio = $item->biblio; + my $branch = $item->holdingbranch; + my $category = $builder->build({ source => 'Category' })->{ categorycode }; + my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category, branchcode => $branch } }); + my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category, branchcode => $branch } }); + t::lib::Mocks::mock_userenv({ patron => $patron1 }); + + Koha::CirculationRules->set_rules({ + branchcode => undef, + categorycode => undef, + itemtype => undef, + rules => { + 'recall_due_date_interval' => undef, + 'recalls_allowed' => 10, + } + }); + + C4::Circulation::AddIssue( $patron2->unblessed, $item->barcode ); + + warning_like { + Koha::Recalls->add_recall({ + patron => $patron1, + biblio => $biblio, + branchcode => $branch, + item => undef, + expirationdate => undef, + interface => 'COMMANDLINE', + }); + } + qr/after_recall_action called with action: add, ref: Koha::Recall/, + '->add_recall calls the after_recall_action hook with action add'; + + $schema->storage->txn_rollback; + Koha::Plugins::Methods->delete; +}; diff --git a/t/lib/plugins/Koha/Plugin/Test.pm b/t/lib/plugins/Koha/Plugin/Test.pm index eee99859d1..87e4fcd094 100644 --- a/t/lib/plugins/Koha/Plugin/Test.pm +++ b/t/lib/plugins/Koha/Plugin/Test.pm @@ -348,6 +348,38 @@ sub background_tasks { }; } +sub after_account_action { + my ( $self, $params ) = @_; + + my $action = $params->{action}; + my $line = $params->{payload}->{line}; + my $type = $params->{payload}->{type}; + + Koha::Exception->throw( + "after_account_action called with action: $action, type: $type, ref: " . ref($line) ); +} + +sub before_index_action { + my ( $self, $params ) = @_; + + my $action = $params->{action}; + my $engine = $params->{payload}->{engine}; + my $records = $params->{payload}->{records}; + + Koha::Exception->throw( + "before_index_action called with action: $action, engine: $engine, ref: " . ref($records) ); +} + +sub after_recall_action { + my ( $self, $params ) = @_; + + my $action = $params->{action}; + my $recall = $params->{payload}->{recall}; + + Koha::Exception->throw( + "after_recall_action called with action: $action, ref: " . ref($recall) ); +} + sub _private_sub { return ""; } -- 2.37.1