use Modern::Perl;
use File::Basename qw/basename/;
+
+use C4::Circulation qw( AddIssue AddReturn );
+
use Koha::Database;
use Koha::Illrequestattributes;
use Koha::Illrequest::Config;
use Koha::ItemTypes;
use Koha::Items;
use Koha::Libraries;
+use Koha::MessageAttributes;
+use Koha::Notice::Templates;
use Koha::AuthorisedValueCategories;
use Koha::AuthorisedValues;
use t::lib::Mocks;
use Test::Deep qw/ cmp_deeply ignore /;
use Test::Warn;
-use Test::More tests => 12;
+use Test::More tests => 14;
my $schema = Koha::Database->new->schema;
my $builder = t::lib::TestBuilder->new;
subtest 'Status Graph tests' => sub {
- plan tests => 5;
+ plan tests => 6;
$schema->storage->txn_begin;
"new node + core_status_graph = bigger status graph"
) || diag explain $new_graph;
+ # Create a duplicate node
+ my $dupe_node = {
+ REQ => {
+ prev_actions => [ 'NEW', 'REQREV', 'QUEUED', 'CANCREQ' ],
+ id => 'REQ',
+ name => 'Requested',
+ ui_method_name => 'Confirm request dupe',
+ method => 'confirm',
+ next_actions => [ 'REQREV', 'COMP', 'CHK' ],
+ ui_method_icon => 'fa-check',
+ }
+ };
+ # Add the dupe node to the core_status_grpah
+ my $dupe_graph = $illrq_obj->_status_graph_union( $illrq_obj->_core_status_graph, $dupe_node);
+ # Compare the updated graph to the expected graph
+ # The structure we compare against here is just a copy of the structure found
+ # in Koha::Illrequest::_core_status_graph() + the new node we created above
+ cmp_deeply( $dupe_graph,
+ {
+ NEW => {
+ prev_actions => [ ], # Actions containing buttons
+ # leading to this status
+ id => 'NEW', # ID of this status
+ name => 'New request', # UI name of this status
+ ui_method_name => 'New request', # UI name of method leading
+ # to this status
+ method => 'create', # method to this status
+ next_actions => [ 'REQ', 'GENREQ', 'KILL' ], # buttons to add to all
+ # requests with this status
+ ui_method_icon => 'fa-plus', # UI Style class
+ },
+ REQ => {
+ prev_actions => [ 'NEW', 'REQREV', 'QUEUED', 'CANCREQ' ],
+ id => 'REQ',
+ name => 'Requested',
+ ui_method_name => 'Confirm request dupe',
+ method => 'confirm',
+ next_actions => [ 'REQREV', 'COMP', 'CHK' ],
+ ui_method_icon => 'fa-check',
+ },
+ GENREQ => {
+ prev_actions => [ 'NEW', 'REQREV' ],
+ id => 'GENREQ',
+ name => 'Requested from partners',
+ ui_method_name => 'Place request with partners',
+ method => 'generic_confirm',
+ next_actions => [ 'COMP', 'CHK' ],
+ ui_method_icon => 'fa-send-o',
+ },
+ REQREV => {
+ prev_actions => [ 'REQ' ],
+ id => 'REQREV',
+ name => 'Request reverted',
+ ui_method_name => 'Revert Request',
+ method => 'cancel',
+ next_actions => [ 'REQ', 'GENREQ', 'KILL' ],
+ ui_method_icon => 'fa-times',
+ },
+ QUEUED => {
+ prev_actions => [ ],
+ id => 'QUEUED',
+ name => 'Queued request',
+ ui_method_name => 0,
+ method => 0,
+ next_actions => [ 'REQ', 'KILL' ],
+ ui_method_icon => 0,
+ },
+ CANCREQ => {
+ prev_actions => [ 'NEW' ],
+ id => 'CANCREQ',
+ name => 'Cancellation requested',
+ ui_method_name => 0,
+ method => 0,
+ next_actions => [ 'KILL', 'REQ' ],
+ ui_method_icon => 0,
+ },
+ COMP => {
+ prev_actions => [ 'REQ' ],
+ id => 'COMP',
+ name => 'Completed',
+ ui_method_name => 'Mark completed',
+ method => 'mark_completed',
+ next_actions => [ 'CHK' ],
+ ui_method_icon => 'fa-check',
+ },
+ KILL => {
+ prev_actions => [ 'QUEUED', 'REQREV', 'NEW', 'CANCREQ' ],
+ id => 'KILL',
+ name => 0,
+ ui_method_name => 'Delete request',
+ method => 'delete',
+ next_actions => [ ],
+ ui_method_icon => 'fa-trash',
+ },
+ CHK => {
+ prev_actions => [ 'REQ', 'GENREQ', 'COMP' ],
+ id => 'CHK',
+ name => 'Checked out',
+ ui_method_name => 'Check out',
+ needs_prefs => [ 'CirculateILL' ],
+ needs_perms => [ 'user_circulate_circulate_remaining_permissions' ],
+ needs_all => ignore(),
+ method => 'check_out',
+ next_actions => [ ],
+ ui_method_icon => 'fa-upload',
+ },
+ RET => {
+ prev_actions => [ 'CHK' ],
+ id => 'RET',
+ name => 'Returned to library',
+ ui_method_name => 'Check in',
+ method => 'check_in',
+ next_actions => [ 'COMP' ],
+ ui_method_icon => 'fa-download',
+ }
+ },
+ "new node + core_status_graph = bigger status graph"
+ ) || diag explain $dupe_graph;
+
$schema->storage->txn_rollback;
};
},
"Backend cancel: arbitrary stage.");
+ # backend_illview
+ $backend->set_series('illview', { stage => '', method => 'illview' });
+ is_deeply($illrq->backend_illview({test => 1}), 0,
+ "Backend illview optional method.");
+
# backend_update_status
$backend->set_series('update_status', { stage => 'bar', method => 'update_status' });
is_deeply($illrq->backend_update_status({test => 1}),
}) }
"Generic confirm: missing to dies OK.";
- dies_ok { $illrq->generic_confirm({
- current_branchcode => $illbrn->{branchcode},
- partners => $partner1->{email},
- stage => 'draft'
- }) }
- "Generic confirm: missing from dies OK.";
-
$schema->storage->txn_rollback;
};
subtest 'Helpers' => sub {
- plan tests => 7;
+ plan tests => 21;
$schema->storage->txn_begin;
my $backend = Test::MockObject->new;
$backend->set_isa('Koha::Illbackends::Mock');
$backend->set_always('name', 'Mock');
+ $backend->mock(
+ 'metadata',
+ sub {
+ my ( $self, $rq ) = @_;
+ return {
+ title => 'mytitle',
+ author => 'myauthor'
+ }
+ }
+ );
my $config = Test::MockObject->new;
$config->set_always('backend_dir', "/tmp");
source => 'Borrower',
value => { categorycode => "A" }
});
+ # Create a mocked branch with no email addressed defined
+ my $illbrn = $builder->build({
+ source => 'Branch',
+ value => {
+ branchcode => 'HDE',
+ branchemail => "",
+ branchillemail => "",
+ branchreplyto => ""
+ }
+ });
my $illrq = $builder->build({
source => 'Illrequest',
- value => { branchcode => "CPL", borrowernumber => $patron->{borrowernumber} }
+ value => { branchcode => "HDE", borrowernumber => $patron->{borrowernumber} }
});
my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id});
$illrq_obj->_config($config);
# getPrefix
$config->set_series('getPrefixes',
- { CPL => "TEST", TSL => "BAR", default => "DEFAULT" },
+ { HDE => "TEST", TSL => "BAR", default => "DEFAULT" },
{ A => "ATEST", C => "CBAR", default => "DEFAULT" });
- is($illrq_obj->getPrefix({ brw_cat => "UNKNOWN", branch => "CPL" }), "TEST",
+ is($illrq_obj->getPrefix({ brw_cat => "UNKNOWN", branch => "HDE" }), "TEST",
"getPrefix: branch");
$config->set_series('getPrefixes',
- { CPL => "TEST", TSL => "BAR", default => "DEFAULT" },
+ { HDE => "TEST", TSL => "BAR", default => "DEFAULT" },
{ A => "ATEST", C => "CBAR", default => "DEFAULT" });
is($illrq_obj->getPrefix({ branch => "UNKNOWN" }), "",
"getPrefix: default");
# id_prefix
$config->set_series('getPrefixes',
- { CPL => "TEST", TSL => "BAR", default => "DEFAULT" },
+ { HDE => "TEST", TSL => "BAR", default => "DEFAULT" },
{ AB => "ATEST", CD => "CBAR", default => "DEFAULT" });
is($illrq_obj->id_prefix, "TEST-", "id_prefix: branch");
$config->set_series('getPrefixes',
- { CPLT => "TEST", TSLT => "BAR", default => "DEFAULT" },
+ { HDET => "TEST", TSLT => "BAR", default => "DEFAULT" },
{ AB => "ATEST", CD => "CBAR", default => "DEFAULT" });
is($illrq_obj->id_prefix, "", "id_prefix: default");
$illrq_obj->status('CANCREQ')->store;
is($illrq_obj->requires_moderation, 'CANCREQ', "requires_moderation: Yes.");
+ #send_patron_notice
+ my $attr = Koha::MessageAttributes->find({ message_name => 'Ill_ready' });
+ C4::Members::Messaging::SetMessagingPreference({
+ borrowernumber => $patron->{borrowernumber},
+ message_attribute_id => $attr->message_attribute_id,
+ message_transport_types => ['email']
+ });
+ my $return_patron = $illrq_obj->send_patron_notice('ILL_PICKUP_READY');
+ my $notice = $schema->resultset('MessageQueue')->search({
+ letter_code => 'ILL_PICKUP_READY',
+ message_transport_type => 'email',
+ borrowernumber => $illrq_obj->borrowernumber
+ })->next()->letter_code;
+ is_deeply(
+ $return_patron,
+ { result => { success => ['email'], fail => [] } },
+ "Correct return when notice created"
+ );
+ is($notice, 'ILL_PICKUP_READY' ,"Notice is correctly created");
+
+ my $return_patron_fail = $illrq_obj->send_patron_notice();
+ is_deeply(
+ $return_patron_fail,
+ { error => 'notice_no_type' },
+ "Correct error when missing type"
+ );
+
+ #send_staff_notice
+ # Specify that no staff notices should be send
+ t::lib::Mocks::mock_preference('ILLSendStaffNotices', '');
+ my $return_staff_cancel_fail =
+ $illrq_obj->send_staff_notice('ILL_REQUEST_CANCEL');
+ is_deeply(
+ $return_staff_cancel_fail,
+ { error => 'notice_not_enabled' },
+ "Does not send notices that are not enabled"
+ );
+ my $queue = $schema->resultset('MessageQueue')->search({
+ letter_code => 'ILL_REQUEST_CANCEL'
+ });
+ is($queue->count, 0, "Notice is not queued");
+
+ # Specify that the cancel notice can be sent
+ t::lib::Mocks::mock_preference('ILLSendStaffNotices', 'ILL_REQUEST_CANCEL');
+ my $return_staff_cancel = $illrq_obj->send_staff_notice(
+ 'ILL_REQUEST_CANCEL'
+ );
+ is_deeply(
+ $return_staff_cancel,
+ { success => 'notice_queued' },
+ "Correct return when staff notice created"
+ );
+ $queue = $schema->resultset('MessageQueue')->search({
+ letter_code => 'ILL_REQUEST_CANCEL'
+ });
+ is($queue->count, 1, "Notice queued as expected");
+
+ my $return_staff_fail = $illrq_obj->send_staff_notice();
+ is_deeply(
+ $return_staff_fail,
+ { error => 'notice_no_type' },
+ "Correct error when missing type"
+ );
+ $queue = $schema->resultset('MessageQueue')->search({
+ letter_code => 'ILL_REQUEST_CANCEL'
+ });
+ is($queue->count, 1, "Notice is not queued");
+
+ #get_notice
+ my $not = $illrq_obj->get_notice({
+ notice_code => 'ILL_REQUEST_CANCEL',
+ transport => 'email'
+ });
+
+ # We test the properties of the hashref separately because the random
+ # hash ordering of the metadata means we can't test the entire thing
+ # with is_deeply
+ ok(
+ $not->{module} eq 'ill',
+ 'Correct module return from get_notice'
+ );
+ ok(
+ $not->{name} eq 'ILL request cancelled',
+ 'Correct name return from get_notice'
+ );
+ ok(
+ $not->{message_transport_type} eq 'email',
+ 'Correct message_transport_type return from get_notice'
+ );
+ ok(
+ $not->{title} eq 'Interlibrary loan request cancelled',
+ 'Correct title return from get_notice'
+ );
+ $not->{content} =~ s/\s//g;
+
+ is(
+ $not->{content},"Thepatronforinterlibraryloansrequest" . $illrq_obj->id . ",withthefollowingdetails,hasrequestedcancellationofthisILLrequest:-author:myauthor-title:mytitle",
+ 'Correct content returned from get_notice with metadata correctly ordered'
+ );
+
$schema->storage->txn_rollback;
};
$schema->storage->txn_rollback;
};
+subtest 'Checking out with custom due date' => sub {
+ plan tests => 1;
+ $schema->storage->txn_begin;
+
+ my $library = $builder->build_object({ class => 'Koha::Libraries' });
+ my $patron = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => { category_type => 'x' }
+ });
+ my $biblio = $builder->build_sample_biblio();
+ my $itemtype_loanable = $builder->build_object({
+ class => 'Koha::ItemTypes',
+ value => {
+ notforloan => 0
+ }
+ });
+ my $request = $builder->build_object({
+ class => 'Koha::Illrequests',
+ value => {
+ borrowernumber => $patron->borrowernumber,
+ biblio_id => $biblio->biblionumber
+ }
+ });
+
+ t::lib::Mocks::mock_userenv({ branchcode => $library->branchcode });
+ my $duedate = '2099-05-21 00:00:00';
+ my $form_stage_loanable = $request->check_out({
+ stage => 'form',
+ item_type => $itemtype_loanable->itemtype,
+ branchcode => $library->branchcode,
+ duedate => $duedate
+ });
+ is($patron->checkouts->next->date_due, $duedate, "Custom due date was used");
+
+ $schema->storage->txn_rollback;
+};
+
subtest 'Checking Limits' => sub {
plan tests => 30;
$schema->storage->txn_rollback;
};
+
+subtest 'Checking in hook' => sub {
+
+ plan tests => 2;
+
+ $schema->storage->txn_begin;
+
+ # Build infrastructure
+ my $backend = Test::MockObject->new;
+ $backend->set_isa('Koha::Illbackends::Mock');
+ $backend->set_always('name', 'Mock');
+
+ my $config = Test::MockObject->new;
+ $config->set_always('backend_dir', "/tmp");
+
+ my $item = $builder->build_sample_item();
+ my $patron = $builder->build_object({ class => 'Koha::Patrons' });
+
+ t::lib::Mocks::mock_userenv(
+ {
+ patron => $patron,
+ branchcode => $patron->branchcode
+ }
+ );
+
+ my $illrq = $builder->build_object(
+ {
+ class => 'Koha::Illrequests',
+ value => {
+ biblio_id => $item->biblio->biblionumber,
+ status => 'NEW'
+ }
+ }
+ );
+
+ $illrq->_config($config);
+ $illrq->_backend($backend);
+
+ t::lib::Mocks::mock_preference('CirculateILL', 1);
+
+ # Add an issue
+ AddIssue( $patron->unblessed, $item->barcode );
+ # Make the item withdrawn so checking-in is rejected
+ t::lib::Mocks::mock_preference('BlockReturnOfWithdrawnItems', 1);
+ $item->set({ withdrawn => 1 })->store;
+ AddReturn( $item->barcode, $patron->branchcode );
+ # refresh request
+ $illrq->discard_changes;
+ isnt( $illrq->status, 'RET' );
+
+ # allow the check-in
+ $item->set({ withdrawn => 0 })->store;
+ AddReturn( $item->barcode, $patron->branchcode );
+ # refresh request
+ $illrq->discard_changes;
+ is( $illrq->status, 'RET' );
+
+ $schema->storage->txn_rollback;
+};