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