Bug 31196: Remove 'default_value_for_mod_marc-' clear_from_cache calls
[koha-ffzg.git] / t / db_dependent / Koha / Biblio.t
index 73dcb2a..98eac9b 100755 (executable)
@@ -17,7 +17,7 @@
 
 use Modern::Perl;
 
-use Test::More tests => 19;
+use Test::More tests => 22; # +1
 use Test::Warn;
 
 use C4::Biblio qw( AddBiblio ModBiblio ModBiblioMarc );
@@ -29,7 +29,7 @@ use Koha::Acquisition::Orders;
 use Koha::AuthorisedValueCategories;
 use Koha::AuthorisedValues;
 use Koha::MarcSubfieldStructures;
-use Koha::Exceptions::Exception;
+use Koha::Exception;
 
 use MARC::Field;
 use MARC::Record;
@@ -126,7 +126,7 @@ subtest 'hidden_in_opac() tests' => sub {
 
 subtest 'items() tests' => sub {
 
-    plan tests => 4;
+    plan tests => 3;
 
     $schema->storage->txn_begin;
 
@@ -141,9 +141,6 @@ subtest 'items() tests' => sub {
     is( ref($items), 'Koha::Items', 'Returns a Koha::Items resultset' );
     is( $items->count, 2, 'Two items in resultset' );
 
-    my @items = $biblio->items->as_list;
-    is( scalar @items, 2, 'Same result, but in list context' );
-
     $schema->storage->txn_rollback;
 
 };
@@ -521,7 +518,7 @@ subtest 'get_marc_components() tests' => sub {
     my $host_biblio = Koha::Biblios->find($host_bibnum);
     t::lib::Mocks::mock_preference( 'SearchEngine', 'Zebra' );
     my $search_mod = Test::MockModule->new( 'Koha::SearchEngine::Zebra::Search' );
-    $search_mod->mock( 'simple_search_compat', \&search_component_record2 );
+    $search_mod->mock( 'search_compat', \&search_component_record2 );
 
     my $components = $host_biblio->get_marc_components;
     is( ref($components), 'ARRAY', 'Return type is correct' );
@@ -532,8 +529,8 @@ subtest 'get_marc_components() tests' => sub {
         '->get_marc_components returns an empty ARRAY'
     );
 
-    $search_mod->unmock( 'simple_search_compat');
-    $search_mod->mock( 'simple_search_compat', \&search_component_record1 );
+    $search_mod->unmock( 'search_compat');
+    $search_mod->mock( 'search_compat', \&search_component_record1 );
     my $component_record = component_record1()->as_xml();
 
     is_deeply(
@@ -541,53 +538,64 @@ subtest 'get_marc_components() tests' => sub {
         [$component_record],
         '->get_marc_components returns the related component part record'
     );
-    $search_mod->unmock( 'simple_search_compat');
+    $search_mod->unmock( 'search_compat');
 
-    $search_mod->mock( 'simple_search_compat',
-        sub { Koha::Exceptions::Exception->throw("error searching analytics") }
+    $search_mod->mock( 'search_compat',
+        sub { Koha::Exception->throw("error searching analytics") }
     );
     warning_like { $components = $host_biblio->get_marc_components }
-        qr{^Warning from simple_search_compat: 'error searching analytics'};
+        qr{Warning from search_compat: .* 'error searching analytics'};
 
     is_deeply(
-        $host_biblio->messages,
+        $host_biblio->object_messages,
         [
             {
                 type    => 'error',
                 message => 'component_search',
-                payload => "error searching analytics"
+                payload => "Exception 'Koha::Exception' thrown 'error searching analytics'\n"
             }
         ]
     );
-    $search_mod->unmock( 'simple_search_compat');
+    $search_mod->unmock( 'search_compat');
 
     $schema->storage->txn_rollback;
 };
 
 subtest 'get_components_query' => sub {
-    plan tests => 3;
+    plan tests => 6;
 
     my $biblio = $builder->build_sample_biblio();
     my $biblionumber = $biblio->biblionumber;
     my $record = $biblio->metadata->record;
 
     t::lib::Mocks::mock_preference( 'UseControlNumber', '0' );
-    is($biblio->get_components_query, "Host-item:(Some boring read)", "UseControlNumber disabled");
+    t::lib::Mocks::mock_preference( 'ComponentSortField', 'author' );
+    t::lib::Mocks::mock_preference( 'ComponentSortOrder', 'za' );
+    my ( $comp_q, $comp_s ) = $biblio->get_components_query;
+    is($comp_q, 'Host-item:("Some boring read")', "UseControlNumber disabled");
+    is($comp_s, "author_za", "UseControlNumber disabled sort is correct");
 
     t::lib::Mocks::mock_preference( 'UseControlNumber', '1' );
+    t::lib::Mocks::mock_preference( 'ComponentSortOrder', 'az' );
     my $marc_001_field = MARC::Field->new('001', $biblionumber);
     $record->append_fields($marc_001_field);
     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
     $biblio = Koha::Biblios->find( $biblio->biblionumber);
 
-    is($biblio->get_components_query, "(rcn:$biblionumber AND (bib-level:a OR bib-level:b))", "UseControlNumber enabled without MarcOrgCode");
+    ( $comp_q, $comp_s ) = $biblio->get_components_query;
+    is($comp_q, "(rcn:$biblionumber AND (bib-level:a OR bib-level:b))", "UseControlNumber enabled without MarcOrgCode");
+    is($comp_s, "author_az", "UseControlNumber enabled without MarcOrgCode sort is correct");
 
     my $marc_003_field = MARC::Field->new('003', 'OSt');
     $record->append_fields($marc_003_field);
     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
     $biblio = Koha::Biblios->find( $biblio->biblionumber);
 
-    is($biblio->get_components_query, "(((rcn:$biblionumber AND cni:OSt) OR rcn:\"OSt $biblionumber\") AND (bib-level:a OR bib-level:b))", "UseControlNumber enabled with MarcOrgCode");
+    t::lib::Mocks::mock_preference( 'ComponentSortField', 'title' );
+    t::lib::Mocks::mock_preference( 'ComponentSortOrder', 'asc' );
+    ( $comp_q, $comp_s ) = $biblio->get_components_query;
+    is($comp_q, "(((rcn:$biblionumber AND cni:OSt) OR rcn:\"OSt $biblionumber\") AND (bib-level:a OR bib-level:b))", "UseControlNumber enabled with MarcOrgCode");
+    is($comp_s, "title_asc", "UseControlNumber enabled with MarcOrgCode sort if correct");
 };
 
 subtest 'orders() and active_orders() tests' => sub {
@@ -671,7 +679,7 @@ subtest 'subscriptions() tests' => sub {
 };
 
 subtest 'get_marc_notes() MARC21 tests' => sub {
-    plan tests => 13;
+    plan tests => 14;
 
     $schema->storage->txn_begin;
 
@@ -684,33 +692,44 @@ subtest 'get_marc_notes() MARC21 tests' => sub {
         MARC::Field->new( '505', '', '', a => 'Note2', u => 'http://someserver.com' ),
         MARC::Field->new( '520', '', '', a => 'Note3 skipped' ),
         MARC::Field->new( '541', '0', '', a => 'Note4 skipped on opac' ),
-        MARC::Field->new( '541', '', '', a => 'Note5' ),
+        MARC::Field->new( '544', '', '', a => 'Note5' ),
         MARC::Field->new( '590', '', '', a => 'CODE' ),
+        MARC::Field->new( '545', '', '', a => 'Invisible on OPAC' ),
     );
 
     Koha::AuthorisedValueCategory->new({ category_name => 'TEST' })->store;
-    Koha::AuthorisedValue->new({ category => 'TEST', authorised_value => 'CODE', lib => 'Description should show', lib_opac => 'Description should show OPAC' })->store;
+    Koha::AuthorisedValue->new(
+        {
+            category         => 'TEST',
+            authorised_value => 'CODE',
+            lib              => 'Description should show',
+            lib_opac         => 'Description should show OPAC'
+        }
+    )->store;
     my $mss = Koha::MarcSubfieldStructures->find({tagfield => "590", tagsubfield => "a", frameworkcode => $biblio->frameworkcode });
     $mss->update({ authorised_value => "TEST" });
 
+    $mss = Koha::MarcSubfieldStructures->find({tagfield => "545", tagsubfield => "a", frameworkcode => $biblio->frameworkcode });
+    $mss->update({ hidden => 1 });
+
     my $cache = Koha::Caches->get_instance;
     $cache->clear_from_cache("MarcStructure-0-");
     $cache->clear_from_cache("MarcStructure-1-");
-    $cache->clear_from_cache("default_value_for_mod_marc-");
     $cache->clear_from_cache("MarcSubfieldStructure-");
 
     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
     $biblio = Koha::Biblios->find( $biblio->biblionumber);
 
-    my $notes = $biblio->get_marc_notes({ marcflavour => 'MARC21' });
+    my $notes = $biblio->get_marc_notes;
     is( $notes->[0]->{marcnote}, 'Note1', 'First note' );
     is( $notes->[1]->{marcnote}, 'Note2', 'Second note' );
     is( $notes->[2]->{marcnote}, 'http://someserver.com', 'URL separated' );
-    is( $notes->[3]->{marcnote}, 'Note4 skipped on opac',"Not shows if not opac" );
+    is( $notes->[3]->{marcnote}, 'Note4 skipped on opac',"Note shows if not opac (Hidden by Indicator)" );
     is( $notes->[4]->{marcnote}, 'Note5', 'Fifth note' );
     is( $notes->[5]->{marcnote}, 'Description should show', 'Authorised value is correctly parsed to show description rather than code' );
-    is( @$notes, 6, 'No more notes' );
-    $notes = $biblio->get_marc_notes({ marcflavour => 'MARC21', opac => 1 });
+    is( $notes->[6]->{marcnote}, 'Invisible on OPAC', 'Note shows if not opac (Hidden by framework)' );
+    is( @$notes, 7, 'No more notes' );
+    $notes = $biblio->get_marc_notes({ opac => 1 });
     is( $notes->[0]->{marcnote}, 'Note1', 'First note' );
     is( $notes->[1]->{marcnote}, 'Note2', 'Second note' );
     is( $notes->[2]->{marcnote}, 'http://someserver.com', 'URL separated' );
@@ -720,7 +739,6 @@ subtest 'get_marc_notes() MARC21 tests' => sub {
 
     $cache->clear_from_cache("MarcStructure-0-");
     $cache->clear_from_cache("MarcStructure-1-");
-    $cache->clear_from_cache("default_value_for_mod_marc-");
     $cache->clear_from_cache("MarcSubfieldStructure-");
 
     $schema->storage->txn_rollback;
@@ -732,6 +750,7 @@ subtest 'get_marc_notes() UNIMARC tests' => sub {
     $schema->storage->txn_begin;
 
     t::lib::Mocks::mock_preference( 'NotesToHide', '310' );
+    t::lib::Mocks::mock_preference( 'marcflavour', 'UNIMARC' );
 
     my $biblio = $builder->build_sample_biblio;
     my $record = $biblio->metadata->record;
@@ -747,6 +766,7 @@ subtest 'get_marc_notes() UNIMARC tests' => sub {
     is( $notes->[1]->{marcnote}, 'Note2', 'Second note' );
     is( @$notes, 2, 'No more notes' );
 
+    t::lib::Mocks::mock_preference( 'marcflavour', 'MARC21' );
     $schema->storage->txn_rollback;
 };
 
@@ -859,6 +879,175 @@ subtest 'current_checkouts() and old_checkouts() tests' => sub {
     $schema->storage->txn_rollback;
 };
 
+subtest 'get_marc_contributors() tests' => sub {
+
+    plan tests => 2;
+
+    $schema->storage->txn_begin;
+
+    my $biblio = $builder->build_sample_biblio({ author => 'Main author' });
+    my $record = $biblio->metadata->record;
+
+    # add author information
+    my $field = MARC::Field->new('700','1','','a' => 'Jefferson, Thomas');
+    $record->append_fields($field);
+    $field = MARC::Field->new('701','1','','d' => 'Secondary author 2');
+    $record->append_fields($field);
+
+    # get record
+    C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
+    $biblio = Koha::Biblios->find( $biblio->biblionumber );
+
+    is( @{$biblio->get_marc_authors}, 3, 'get_marc_authors retrieves correct number of author subfields' );
+    is( @{$biblio->get_marc_contributors}, 2, 'get_marc_contributors retrieves correct number of author subfields' );
+    $schema->storage->txn_rollback;
+};
+
+subtest 'Recalls tests' => sub {
+
+    plan tests => 13;
+
+    $schema->storage->txn_begin;
+
+    my $item1 = $builder->build_sample_item;
+    my $biblio = $item1->biblio;
+    my $branchcode = $item1->holdingbranch;
+    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
+    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
+    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
+    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
+    t::lib::Mocks::mock_userenv({ patron => $patron1 });
+
+    my $recall1 = Koha::Recall->new(
+        {   patron_id         => $patron1->borrowernumber,
+            created_date      => \'NOW()',
+            biblio_id         => $biblio->biblionumber,
+            pickup_library_id => $branchcode,
+            item_id           => $item1->itemnumber,
+            expiration_date   => undef,
+            item_level        => 1
+        }
+    )->store;
+    my $recall2 = Koha::Recall->new(
+        {   patron_id         => $patron2->borrowernumber,
+            created_date      => \'NOW()',
+            biblio_id         => $biblio->biblionumber,
+            pickup_library_id => $branchcode,
+            item_id           => undef,
+            expiration_date   => undef,
+            item_level        => 0
+        }
+    )->store;
+    my $recall3 = Koha::Recall->new(
+        {   patron_id         => $patron3->borrowernumber,
+            created_date      => \'NOW()',
+            biblio_id         => $biblio->biblionumber,
+            pickup_library_id => $branchcode,
+            item_id           => $item1->itemnumber,
+            expiration_date   => undef,
+            item_level        => 1
+        }
+    )->store;
+
+    my $recalls = $biblio->recalls;
+    is( $recalls->count, 3, 'Correctly get number of recalls for biblio' );
+
+    $recall1->set_cancelled;
+    $recall2->set_expired({ interface => 'COMMANDLINE' });
+
+    is( $recalls->count, 3, 'Correctly get number of recalls for biblio' );
+    is( $recalls->filter_by_current->count, 1, 'Correctly get number of active recalls for biblio' );
+
+    t::lib::Mocks::mock_preference('UseRecalls', 0);
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
+
+    t::lib::Mocks::mock_preference("UseRecalls", 1);
+    $item1->update({ notforloan => 1 });
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
+
+    $item1->update({ notforloan => 0 });
+    Koha::CirculationRules->set_rules({
+        branchcode => $branchcode,
+        categorycode => $patron1->categorycode,
+        itemtype => $item1->effective_itemtype,
+        rules => {
+            recalls_allowed => 0,
+            recalls_per_record => 1,
+            on_shelf_recalls => 'all',
+        },
+    });
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
+
+    Koha::CirculationRules->set_rules({
+        branchcode => $branchcode,
+        categorycode => $patron1->categorycode,
+        itemtype => $item1->effective_itemtype,
+        rules => {
+            recalls_allowed => 1,
+            recalls_per_record => 1,
+            on_shelf_recalls => 'all',
+        },
+    });
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
+
+    $recall1->set_cancelled;
+    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
+
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
+
+    Koha::CirculationRules->set_rules({
+        branchcode => $branchcode,
+        categorycode => $patron1->categorycode,
+        itemtype => $item1->effective_itemtype,
+        rules => {
+            recalls_allowed => 1,
+            recalls_per_record => 1,
+            on_shelf_recalls => 'any',
+        },
+    });
+    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
+
+    $recall2->set_cancelled;
+    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
+    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
+
+    $item1->update({ withdrawn => 1 });
+    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
+
+    $schema->storage->txn_rollback;
+};
+
+subtest 'item_groups() tests' => sub {
+
+    plan tests => 6;
+
+    $schema->storage->txn_begin;
+
+    my $biblio = $builder->build_sample_biblio();
+
+    my @item_groups = $biblio->item_groups->as_list;
+    is( scalar(@item_groups), 0, 'Got zero item groups');
+
+    my $item_group_1 = Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id } )->store();
+
+    @item_groups = $biblio->item_groups->as_list;
+    is( scalar(@item_groups), 1, 'Got one item group');
+    is( $item_groups[0]->id, $item_group_1->id, 'Got correct item group');
+
+    my $item_group_2 = Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id } )->store();
+
+    @item_groups = $biblio->item_groups->as_list;
+    is( scalar(@item_groups), 2, 'Got two item groups');
+    is( $item_groups[0]->id, $item_group_1->id, 'Got correct item group 1');
+    is( $item_groups[1]->id, $item_group_2->id, 'Got correct item group 2');
+
+    $schema->storage->txn_rollback;
+};
+
 sub component_record1 {
     my $marc = MARC::Record->new;
     $marc->append_fields(
@@ -870,12 +1059,12 @@ sub component_record1 {
 }
 sub search_component_record1 {
     my @results = ( component_record1()->as_xml() );
-    return ( undef, \@results, 1 );
+    return ( undef, { biblioserver => { RECORDS => \@results, hits => 1 } }, 1 );
 }
 
 sub search_component_record2 {
     my @results;
-    return ( undef, \@results, 0 );
+    return ( undef, { biblioserver => { RECORDS => \@results, hits => 0 } }, 0 );
 }
 
 sub host_record {