Bug 23916: Add unit tests
[koha.git] / t / db_dependent / Serials.t
1 #!/usr/bin/perl
2 #
3 # This Koha test module is a stub!
4 # Add more tests here!!!
5
6 use Modern::Perl;
7 use YAML;
8
9 use C4::Serials;
10 use C4::Serials::Frequency;
11 use C4::Serials::Numberpattern;
12 use C4::Debug;
13 use C4::Biblio;
14 use C4::Budgets;
15 use C4::Items;
16 use Koha::Database;
17 use Koha::DateUtils;
18 use Koha::Acquisition::Booksellers;
19 use t::lib::Mocks;
20 use t::lib::TestBuilder;
21 use Test::More tests => 49;
22
23 BEGIN {
24     use_ok('C4::Serials');
25 }
26
27 my $schema = Koha::Database->new->schema;
28 $schema->storage->txn_begin;
29 my $dbh = C4::Context->dbh;
30
31 my $builder = t::lib::TestBuilder->new();
32
33 # This could/should be used for all untested methods
34 my @methods = ('updateClaim');
35 can_ok('C4::Serials', @methods);
36
37 $dbh->do(q|UPDATE marc_subfield_structure SET value_builder="callnumber.pl" where kohafield="items.itemcallnumber" and frameworkcode=''|);
38
39 my $bookseller = Koha::Acquisition::Bookseller->new(
40     {
41         name => "my vendor",
42         address1 => "bookseller's address",
43         phone => "0123456",
44         active => 1
45     }
46 );
47
48 my ($biblionumber, $biblioitemnumber) = AddBiblio(MARC::Record->new, '');
49
50 my $bpid = AddBudgetPeriod({
51     budget_period_startdate   => '2015-01-01',
52     budget_period_enddate     => '2015-12-31',
53     budget_period_description => "budget desc"
54 });
55
56 my $budget_id = AddBudget({
57     budget_code        => "ABCD",
58     budget_amount      => "123.132",
59     budget_name        => "Périodiques",
60     budget_notes       => "This is a note",
61     budget_period_id   => $bpid
62 });
63
64 my $frequency_id = AddSubscriptionFrequency({ description => "Test frequency 1" });
65 my $pattern_id = AddSubscriptionNumberpattern({
66     label => 'Test numberpattern 1',
67     description => 'Description for numberpattern 1',
68     numberingmethod => '{X}',
69     label1 => q{},
70     add1 => 1,
71     every1 => 1,
72     every1 => 1,
73     numbering1 => 1,
74     whenmorethan1 => 1,
75 });
76
77 my $notes = "a\nnote\non\nseveral\nlines";
78 my $internalnotes = 'intnotes';
79 my $subscriptionid = NewSubscription(
80     undef,      "",     undef, undef, $budget_id, $biblionumber,
81     '2013-01-01', $frequency_id, undef, undef,  undef,
82     undef,      undef,  undef, undef, undef, undef,
83     1,          $notes, ,undef, '2013-01-01', undef, $pattern_id,
84     undef,       undef,  0,    $internalnotes,  0,
85     undef, undef, 0,          undef,         '2013-12-31', 0
86 );
87
88 my $subscriptioninformation = GetSubscription( $subscriptionid );
89
90 is( $subscriptioninformation->{notes}, $notes, 'NewSubscription should set notes' );
91 is( $subscriptioninformation->{internalnotes}, $internalnotes, 'NewSubscription should set internalnotes' );
92
93 my $subscription_history = C4::Serials::GetSubscriptionHistoryFromSubscriptionId($subscriptionid);
94 is( $subscription_history->{opacnote}, undef, 'NewSubscription should not set subscriptionhistory opacnotes' );
95 is( $subscription_history->{librariannote}, undef, 'NewSubscription should not set subscriptionhistory librariannotes' );
96
97 my @subscriptions = SearchSubscriptions({string => $subscriptioninformation->{bibliotitle}, orderby => 'title' });
98 isa_ok( \@subscriptions, 'ARRAY' );
99
100 @subscriptions = SearchSubscriptions({ issn => $subscriptioninformation->{issn}, orderby => 'title' });
101 isa_ok( \@subscriptions, 'ARRAY' );
102
103 @subscriptions = SearchSubscriptions({ ean => $subscriptioninformation->{ean}, orderby => 'title' });
104 isa_ok( \@subscriptions, 'ARRAY' );
105
106 @subscriptions = SearchSubscriptions({ biblionumber => $subscriptioninformation->{bibnum}, orderby => 'title' });
107 isa_ok( \@subscriptions, 'ARRAY' );
108
109 my $frequency = GetSubscriptionFrequency($subscriptioninformation->{periodicity});
110 my $old_frequency;
111 if (not $frequency->{unit}) {
112     $old_frequency = $frequency->{id};
113     $frequency->{unit} = "month";
114     $frequency->{unitsperissue} = 1;
115     $frequency->{issuesperunit} = 1;
116     $frequency->{description} = "Frequency created by t/db_dependant/Serials.t";
117     $subscriptioninformation->{periodicity} = AddSubscriptionFrequency($frequency);
118     $subscriptioninformation->{serialsadditems} = 1;
119
120     ModSubscription( @$subscriptioninformation{qw(
121         librarian branchcode aqbooksellerid cost aqbudgetid startdate
122         periodicity firstacquidate irregularity numberpattern locale
123         numberlength weeklength monthlength lastvalue1 innerloop1 lastvalue2
124         innerloop2 lastvalue3 innerloop3 status biblionumber callnumber notes
125         letter manualhistory internalnotes serialsadditems staffdisplaycount
126         opacdisplaycount graceperiod location enddate subscriptionid
127         skip_serialseq
128     )} );
129 }
130 my $expirationdate = GetExpirationDate($subscriptionid) ;
131 ok( $expirationdate, "expiration date is not NULL" );
132
133 ok(C4::Serials::GetSubscriptionHistoryFromSubscriptionId($subscriptionid), 'test getting history from sub-scription');
134
135 my ($serials_count, @serials) = GetSerials($subscriptionid);
136 ok($serials_count > 0, 'Subscription has at least one serial');
137 my $serial = $serials[0];
138
139 isa_ok(C4::Serials::GetSerialInformation($serial->{serialid}), 'HASH', 'test getting Serial Information');
140
141 subtest 'Values should not be erased on editing' => sub {
142
143     plan tests => 1;
144
145     my $biblio = $builder->build_sample_biblio();
146     my $biblionumber = $biblio->biblionumber;
147     my ( $icn_tag, $icn_sf ) = GetMarcFromKohaField( 'items.itemcallnumber' );
148     my ( $it_tag, $it_sf )   = GetMarcFromKohaField( 'items.itype' );
149
150     my $itemtype = $builder->build( { source => 'Itemtype' } )->{itemtype};
151     my $itemcallnumber = 'XXXmy itemcallnumberXXX';
152
153     my $item_record    = MARC::Record->new;
154
155     $item_record->append_fields(
156         MARC::Field->new( '080', '', '', "a" => "default" ),
157         MARC::Field->new(
158             $icn_tag, '', '',
159             $icn_sf => $itemcallnumber,
160             $it_sf  => $itemtype
161         )
162     );
163     my ( undef, undef, $itemnumber ) = C4::Items::AddItemFromMarc( $item_record, $biblionumber );
164     my $serialid = C4::Serials::NewIssue( "serialseq", $subscriptionid, $biblionumber,
165                                           1, undef, undef, "publisheddatetext", "notes", "routingnotes" );
166     C4::Serials::AddItem2Serial( $serialid, $itemnumber );
167     my $serial_info = C4::Serials::GetSerialInformation($serialid);
168     my ($itemcallnumber_info) = grep { $_->{kohafield} eq 'items.itemcallnumber' }
169                                      @{ $serial_info->{items}[0]->{iteminformation} };
170     like( $itemcallnumber_info->{marc_value}, qr|value="$itemcallnumber"| );
171 };
172
173 # Delete created frequency
174 if ($old_frequency) {
175     my $freq_to_delete = $subscriptioninformation->{periodicity};
176     $subscriptioninformation->{periodicity} = $old_frequency;
177
178     ModSubscription( @$subscriptioninformation{qw(
179         librarian branchcode aqbooksellerid cost aqbudgetid startdate
180         periodicity firstacquidate irregularity numberpattern locale
181         numberlength weeklength monthlength lastvalue1 innerloop1 lastvalue2
182         innerloop2 lastvalue3 innerloop3 status biblionumber callnumber notes
183         letter manualhistory internalnotes serialsadditems staffdisplaycount
184         opacdisplaycount graceperiod location enddate subscriptionid
185         skip_serialseq
186     )} );
187
188     DelSubscriptionFrequency($freq_to_delete);
189 }
190
191 # Test calling subs without parameters
192 is(C4::Serials::AddItem2Serial(), undef, 'test adding item to serial');
193 is(C4::Serials::GetFullSubscription(), undef, 'test getting full subscription');
194 is(C4::Serials::PrepareSerialsData(), undef, 'test preparing serial data');
195
196 subtest 'GetSubscriptionsFromBiblionumber' => sub {
197     plan tests => 4;
198
199     is( C4::Serials::GetSubscriptionsFromBiblionumber(),
200         undef, 'test getting subscriptions form biblio number' );
201
202     my $subscriptions = C4::Serials::GetSubscriptionsFromBiblionumber($biblionumber);
203     ModSubscriptionHistory( $subscriptions->[0]->{subscriptionid},
204         undef, undef, $notes, $notes, $notes );
205
206     $subscriptions = C4::Serials::GetSubscriptionsFromBiblionumber($biblionumber);
207     is( $subscriptions->[0]->{opacnote}, $notes,
208         'GetSubscriptionsFromBiblionumber should have returned the opacnote as it is in DB, ie. without br tags'
209     );
210     is( $subscriptions->[0]->{recievedlist}, $notes,
211         'GetSubscriptionsFromBiblionumber should have returned recievedlist as it is in DB, ie. without br tags'
212     );
213     is( $subscriptions->[0]->{missinglist}, $notes,
214         'GetSubscriptionsFromBiblionumber should have returned missinglist as it is in DB, ie. without br tags'
215     );
216 };
217
218 is(C4::Serials::GetSerials(), undef, 'test getting serials when you enter nothing');
219 is(C4::Serials::GetSerials2(), undef, 'test getting serials when you enter nothing');
220
221 is(C4::Serials::GetLatestSerials(), undef, 'test getting lastest serials');
222
223 is(C4::Serials::GetNextSeq(), undef, 'test getting next seq when you enter nothing');
224
225 is(C4::Serials::GetSeq(), undef, 'test getting seq when you enter nothing');
226
227 is(C4::Serials::CountSubscriptionFromBiblionumber(), undef, 'test counting subscription when nothing is entered');
228
229 is(C4::Serials::ModSubscriptionHistory(), undef, 'test modding subscription history');
230
231 is(C4::Serials::ModSerialStatus(),undef, 'test modding serials');
232
233 is(C4::Serials::findSerialsByStatus(), 0, 'test finding serial by status with no parameters');
234
235 is(C4::Serials::NewIssue(), undef, 'test getting 0 when nothing is entered');
236
237 is(C4::Serials::HasSubscriptionStrictlyExpired(), undef, 'test if the subscriptions has expired');
238 is(C4::Serials::HasSubscriptionExpired(), undef, 'test if the subscriptions has expired');
239
240 is(C4::Serials::GetLateOrMissingIssues(), undef, 'test getting last or missing issues');
241
242 subtest 'test_updateClaim' => sub {
243     plan tests => 11;
244
245     my $today = output_pref({ dt => dt_from_string, dateonly => 1 });
246     # Given ... nothing much
247     # When ... Then ...
248     my $result_0 = C4::Serials::updateClaim(undef);
249     is($result_0, undef, 'Got the expected undef from update claim with nothin');
250
251     # Given ... 3 serial. 2 of them updated.
252     my $claimdate_1   = dt_from_string('2001-01-13'); # arbitrary date some time in the past.
253     my $claim_count_1 = 5;
254     my $biblio = $builder->build_sample_biblio;
255     my $serial1 = $builder->build_object(
256         {
257             class => 'Koha::Serials',
258             value => {
259                 serialseq      => 'serialseq',
260                 subscriptionid => $subscriptionid,
261                 status         => 3,
262                 biblionumber   => $biblio->biblionumber,
263                 claimdate      => $claimdate_1,
264                 claims_count   => $claim_count_1,
265             }
266         }
267     );
268     my $serial2 = $builder->build_object(
269         {
270             class => 'Koha::Serials',
271             value => {
272                 serialseq      => 'serialseq',
273                 subscriptionid => $subscriptionid,
274                 status         => 3,
275                 biblionumber   => $biblio->biblionumber,
276                 claimdate      => $claimdate_1,
277                 claims_count   => $claim_count_1,
278             }
279         }
280     );
281     my $serial3 = $builder->build_object(
282         {
283             class => 'Koha::Serials',
284             value => {
285                 serialseq      => 'serialseq',
286                 subscriptionid => $subscriptionid,
287                 status         => 3,
288                 biblionumber   => $biblio->biblionumber,
289                 claimdate      => $claimdate_1,
290                 claims_count   => $claim_count_1,
291             }
292         }
293     );
294
295     # When ...
296     my $result_1 = C4::Serials::updateClaim([$serial1->serialid, $serial2->serialid]);
297
298     # Then ...
299     is($result_1, 2, 'Got the expected 2 from update claim with 2 serial ids');
300
301     my @late_or_missing_issues_1_0 = C4::Serials::GetLateOrMissingIssues(undef, $serial1->serialid);
302     is($late_or_missing_issues_1_0[0]->{claimdate}, $today, 'Got the expected first different claim date from update claim');
303     is($late_or_missing_issues_1_0[0]->{claims_count}, $claim_count_1+1, 'Got the expected first claim count from update claim');
304     is($late_or_missing_issues_1_0[0]->{status}, 7, 'Got the expected first claim status from update claim');
305
306     my @late_or_missing_issues_1_1 = C4::Serials::GetLateOrMissingIssues(undef, $serial2->serialid);
307     is($late_or_missing_issues_1_1[0]->{claimdate}, $today, 'Got the expected second different claim date from update claim');
308     is($late_or_missing_issues_1_1[0]->{claims_count}, $claim_count_1+1, 'Got the expected second claim count from update claim');
309     is($late_or_missing_issues_1_1[0]->{status}, 7, 'Got the expected second claim status from update claim');
310
311     my @late_or_missing_issues_1_2 = C4::Serials::GetLateOrMissingIssues(undef, $serial3->serialid);
312     is($late_or_missing_issues_1_2[0]->{claimdate}, output_pref({ dt => $claimdate_1, dateonly => 1}), 'Got the expected unchanged claim date from update claim');
313     is($late_or_missing_issues_1_2[0]->{claims_count}, $claim_count_1, 'Got the expected unchanged claim count from update claim');
314     is($late_or_missing_issues_1_2[0]->{status}, 3, 'Got the expected unchanged claim status from update claim');
315 };
316
317 is(C4::Serials::check_routing(), undef, 'test checking route');
318 is(C4::Serials::check_routing($subscriptionid), 0, 'There should not have any routing list for the subscription');
319 # TODO really test this check_routing subroutine
320
321 is(C4::Serials::addroutingmember(),undef, 'test adding route member');
322
323
324 # Unit tests for statuses management (Bug 11689)
325 $subscriptionid = NewSubscription(
326     undef,      "",     undef, undef, $budget_id, $biblionumber,
327     '2013-01-01', $frequency_id, undef, undef,  undef,
328     undef,      undef,  undef, undef, undef, undef,
329     1,          $notes,undef, '2013-01-01', undef, $pattern_id,
330     undef,       undef,  0,    $internalnotes,  0,
331     undef, undef, 0,          undef,         '2013-12-31', 0
332 );
333 my $total_issues;
334 ( $total_issues, @serials ) = C4::Serials::GetSerials( $subscriptionid );
335 is( $total_issues, 1, "NewSubscription created a first serial" );
336 is( @serials, 1, "GetSerials returns the serial" );
337 my $subscription = C4::Serials::GetSubscription($subscriptionid);
338 my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
339 ( $total_issues, @serials ) = C4::Serials::GetSerials( $subscriptionid );
340 my $publisheddate = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
341 ( $total_issues, @serials ) = C4::Serials::GetSerials( $subscriptionid );
342 $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
343 my $nextpublisheddate = C4::Serials::GetNextDate($subscription, $publisheddate, $frequency, 1);
344 my @statuses = qw( 2 2 3 3 3 3 3 4 4 41 42 43 44 5 );
345 # Add 14 serials
346 my $counter = 0;
347 for my $status ( @statuses ) {
348     my $serialseq = "No.".$counter;
349     my ( $expected_serial ) = GetSerials2( $subscriptionid, [1] );
350     C4::Serials::ModSerialStatus( $expected_serial->{serialid}, $serialseq, $publisheddate, $publisheddate, $publisheddate, $statuses[$counter], 'an useless note' );
351     $counter++;
352 }
353 # Here we have 15 serials with statuses : 2*2 + 5*3 + 2*4 + 1*41 + 1*42 + 1*43 + 1*44 + 1*5 + 1*1
354 my @serialsByStatus = C4::Serials::findSerialsByStatus(2,$subscriptionid);
355 is(@serialsByStatus,2,"findSerialsByStatus returns all serials with chosen status");
356 ( $total_issues, @serials ) = C4::Serials::GetSerials( $subscriptionid );
357 is( $total_issues, @statuses + 1, "GetSerials returns total_issues" );
358 my @arrived_missing = map { my $status = $_->{status}; ( grep { /^$status$/ } qw( 2 4 41 42 43 44 5 ) ) ? $_ : () } @serials;
359 my @others = map { my $status = $_->{status}; ( grep { /^$status$/ } qw( 2 4 41 42 43 44 5 ) ) ? () : $_ } @serials;
360 is( @arrived_missing, 5, "GetSerials returns 5 arrived/missing by default" );
361 is( @others, 6, "GetSerials returns all serials not arrived and not missing" );
362
363 ( $total_issues, @serials ) = C4::Serials::GetSerials( $subscriptionid, 10 );
364 is( $total_issues, @statuses + 1, "GetSerials returns total_issues" );
365 @arrived_missing = map { my $status = $_->{status}; ( grep { /^$status$/ } qw( 2 4 41 42 43 44 5 ) ) ? $_ : () } @serials;
366 @others = map { my $status = $_->{status}; ( grep { /^$status$/ } qw( 2 4 41 42 43 44 5 ) ) ? () : $_ } @serials;
367 is( @arrived_missing, 9, "GetSerials returns all arrived/missing if count given" );
368 is( @others, 6, "GetSerials returns all serials not arrived and not missing if count given" );
369
370 $subscription = C4::Serials::GetSubscription($subscriptionid); # Retrieve the updated subscription
371
372 my @serialseqs;
373 for my $am ( @arrived_missing ) {
374     if ( grep {/^$am->{status}$/} qw( 4 41 42 43 44 ) ) {
375         push @serialseqs, $am->{serialseq}
376     } elsif ( grep {/^$am->{status}$/} qw( 5 ) ) {
377         push @serialseqs, 'not issued ' . $am->{serialseq};
378     }
379 }
380 is( $subscription->{missinglist}, join('; ', @serialseqs), "subscription missinglist is updated after ModSerialStatus" );
381
382 subtest "Do not generate an expected if one already exists" => sub {
383     plan tests => 2;
384     my ($expected_serial) = GetSerials2( $subscriptionid, [1] );
385
386     #Find serialid for serial with status Expected
387     my $serialexpected = ( C4::Serials::findSerialsByStatus( 1, $subscriptionid ) )[0];
388
389     #delete serial with status Expected
390     C4::Serials::ModSerialStatus( $serialexpected->{serialid}, $serialexpected->{serialseq}, $publisheddate, $publisheddate, $publisheddate, '1', 'an useless note' );
391     @serialsByStatus = C4::Serials::findSerialsByStatus( 1, $subscriptionid );
392     is( @serialsByStatus, 1, "ModSerialStatus delete corectly serial expected and create another if not exist" );
393
394     # add 1 serial with status=Expected 1
395     C4::Serials::ModSerialStatus( $expected_serial->{serialid}, 'NO.20', $publisheddate, $publisheddate, $publisheddate, '1', 'an useless note' );
396
397     #Now we have two serials it have status expected
398     #put status delete for last serial
399     C4::Serials::ModSerialStatus( $serialexpected->{serialid}, $serialexpected->{serialseq}, $publisheddate, $publisheddate, $publisheddate, '1', 'an useless note' );
400
401     #try if create or not another serial with status is expected
402     @serialsByStatus = C4::Serials::findSerialsByStatus( 1, $subscriptionid );
403     is( @serialsByStatus, 1, "ModSerialStatus delete corectly serial expected and not create another if exists" );
404 };
405
406 subtest "PreserveSerialNotes preference" => sub {
407     plan tests => 2;
408     my ($expected_serial) = GetSerials2( $subscriptionid, [1] );
409
410     t::lib::Mocks::mock_preference( 'PreserveSerialNotes', 1 );
411
412     C4::Serials::ModSerialStatus( $expected_serial->{serialid}, 'NO.20', $publisheddate, $publisheddate, $publisheddate, '1', 'an useless note' );
413     @serialsByStatus = C4::Serials::findSerialsByStatus( 1, $subscriptionid );
414     is( $serialsByStatus[0]->{note},$expected_serial->{note}, "note passed through if supposed to");
415
416     t::lib::Mocks::mock_preference( 'PreserveSerialNotes', 0 );
417     $expected_serial = $serialsByStatus[0];
418     C4::Serials::ModSerialStatus( $expected_serial->{serialid}, 'NO.20', $publisheddate, $publisheddate, $publisheddate, '1', 'an useless note' );
419     is( $serialsByStatus[0]->{note},$expected_serial->{note}, "note not passed through if not supposed to");
420
421 };
422
423 subtest "NewSubscription" => sub {
424     plan tests => 1;
425     my $subscriptionid = NewSubscription(
426         "",      "",     "", "", $budget_id, $biblionumber,
427         '2013-01-01', $frequency_id, "", "",  "",
428         "",      "",  "", "", "", "",
429         1,          $notes,"", '2013-01-01', "", $pattern_id,
430         "",       "",  0,    $internalnotes,  0,
431         "", "", 0,          "",         '2013-12-31', 0
432     );
433     ok($subscriptionid, "Sending empty string instead of undef to reflect use of the interface");
434 };