Bug 21478: (QA follow-up) Make sure tests don't fail randomly
[koha.git] / t / db_dependent / Members / IssueSlip.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
19 use Modern::Perl;
20
21 use Data::Dumper; # REMOVEME with diag
22 use Test::More tests => 3;
23 use Test::MockModule;
24 use Test::MockTime qw( set_fixed_time );
25 use t::lib::TestBuilder;
26
27 use C4::Circulation;
28 use C4::Biblio;
29 use C4::Items;
30 use C4::Members;
31
32 use Koha::DateUtils qw( dt_from_string output_pref );
33 use Koha::Library;
34 use Koha::Patrons;
35 use DateTime::Duration;
36
37 use MARC::Record;
38
39 my $schema = Koha::Database->schema;
40 $schema->storage->txn_begin;
41 my $dbh = C4::Context->dbh;
42
43 $dbh->do(q|DELETE FROM issues|);
44 $dbh->do(q|DELETE FROM borrowers|);
45 $dbh->do(q|DELETE FROM items|);
46 $dbh->do(q|DELETE FROM biblio|);
47 $dbh->do(q|DELETE FROM categories|);
48 $dbh->do(q|DELETE FROM letter|);
49
50 my $builder = t::lib::TestBuilder->new;
51 set_fixed_time(CORE::time());
52
53 my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
54 my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
55 my $itemtype     = $builder->build({ source => 'Itemtype' })->{ itemtype };
56
57 my %item_infos = (
58     homebranch    => $branchcode,
59     holdingbranch => $branchcode,
60     itype         => $itemtype
61 );
62
63
64 my $slip_content = <<EOS;
65 Checked out:
66 <checkedout>
67 Title: <<biblio.title>>
68 Barcode: <<items.barcode>>
69 Date due: <<issues.date_due>>
70 </checkedout>
71
72 Overdues:
73 <overdue>
74 Title: <<biblio.title>>
75 Barcode: <<items.barcode>>
76 Date due: <<issues.date_due>>
77 </overdue>
78 EOS
79
80 $dbh->do(q|
81     INSERT INTO letter( module, code, branchcode, name, is_html, title, content, message_transport_type) VALUES ('circulation', 'ISSUESLIP', '', 'Issue Slip', 0, 'Issue Slip', ?, 'email')
82 |, {}, $slip_content);
83
84 my $quick_slip_content = <<EOS;
85 Checked out today:
86 <checkedout>
87 Title: <<biblio.title>>
88 Barcode: <<items.barcode>>
89 Date due: <<issues.date_due>>
90 </checkedout>
91 EOS
92
93 $dbh->do(q|
94     INSERT INTO letter( module, code, branchcode, name, is_html, title, content, message_transport_type) VALUES ('circulation', 'ISSUEQSLIP', '', 'Issue Quick Slip', 0, 'Issue Quick Slip', ?, 'email')
95 |, {}, $quick_slip_content);
96
97 my ( $title1, $title2 ) = ( 'My title 1', 'My title 2' );
98 my ( $barcode1, $barcode2 ) = ('BC0101', 'BC0202' );
99 my $record = MARC::Record->new;
100 $record->append_fields(
101     MARC::Field->new(
102         245, '1', '4',
103             a => $title1,
104     ),
105 );
106 my ($biblionumber1) = AddBiblio( $record, '' );
107 my $itemnumber1 =
108   AddItem( { barcode => $barcode1, %item_infos }, $biblionumber1 );
109
110 $record = MARC::Record->new;
111 $record->append_fields(
112     MARC::Field->new(
113         245, '1', '4',
114             a => $title2,
115     ),
116 );
117 my ($biblionumber2) = AddBiblio( $record, '' );
118 my $itemnumber2 =
119   AddItem( { barcode => $barcode2, %item_infos }, $biblionumber2 );
120
121 my $borrowernumber =
122   Koha::Patron->new({ categorycode => $categorycode, branchcode => $branchcode })->store->borrowernumber;
123 my $borrower = Koha::Patrons->find( $borrowernumber )->unblessed;
124
125 my $module = new Test::MockModule('C4::Context');
126 $module->mock( 'userenv', sub { { branch => $branchcode } } );
127
128 my $today = dt_from_string;
129 my $yesterday = dt_from_string->subtract_duration( DateTime::Duration->new( days => 1 ) );
130
131 subtest 'Issue slip' => sub {
132     plan tests => 3;
133
134     subtest 'Empty slip' => sub {
135         plan tests => 1;
136         my $slip = IssueSlip( $branchcode, $borrowernumber );
137         my $empty_slip = <<EOS;
138 Checked out:
139
140
141 Overdues:
142
143 EOS
144         is( $slip->{content}, $empty_slip, 'No checked out or overdues return an empty slip, it should return undef (FIXME)' );
145     };
146
147     subtest 'Daily loans' => sub {
148         if ( $today->hour == 23 and $today->minute == 59 ) {
149             plan skip_all => "It's 23:59!";
150         } else {
151             plan tests => 2;
152         }
153         # Test 1: No overdue
154         my $today_daily = $today->clone->set( hour => 23, minute => 59 );
155         my $today_daily_as_formatted = output_pref( $today_daily );
156         my $yesterday_daily = $yesterday->clone->set( hour => 23, minute => 59 );
157         my $yesterday_daily_as_formatted = output_pref( $yesterday_daily );
158
159         my ( $date_due, $issue_date, $slip, $expected_slip );
160         $date_due = $today_daily;
161         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
162         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
163         $date_due = $today_daily;
164         $issue_date = $yesterday_daily;
165         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
166
167         # Set timestamps to the same value to avoid a different order
168         Koha::Checkouts->search(
169             { borrowernumber => $borrower->{borrowernumber} }
170         )->update( { timestamp => dt_from_string } );
171
172         $expected_slip = <<EOS;
173 Checked out:
174
175 Title: $title1
176 Barcode: $barcode1
177 Date due: $today_daily_as_formatted
178
179
180 Title: $title2
181 Barcode: $barcode2
182 Date due: $today_daily_as_formatted
183
184
185 Overdues:
186
187 EOS
188         $slip = IssueSlip( $branchcode, $borrowernumber );
189         is( $slip->{content}, $expected_slip , 'IssueSlip should return a slip with 2 checkouts');
190
191         AddReturn( $barcode1, $branchcode );
192         AddReturn( $barcode2, $branchcode );
193
194         # Test 2: 1 Overdue
195         $date_due = $today_daily;
196         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
197         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
198         $date_due = $yesterday_daily;
199         $issue_date = $yesterday_daily;
200         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
201
202         $expected_slip = <<EOS;
203 Checked out:
204
205 Title: $title1
206 Barcode: $barcode1
207 Date due: $today_daily_as_formatted
208
209
210 Overdues:
211
212 Title: $title2
213 Barcode: $barcode2
214 Date due: $yesterday_daily_as_formatted
215
216 EOS
217         $slip = IssueSlip( $branchcode, $borrowernumber );
218         is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue');
219
220         AddReturn( $barcode1, $branchcode );
221         AddReturn( $barcode2, $branchcode );
222     };
223
224     subtest 'Hourly loans' => sub {
225         if ( $today->hour == 23 and $today->minute == 59 ) {
226             plan skip_all => "It's 23:59!";
227         } else {
228             plan tests => 2;
229         }
230         # Test 1: No overdue
231         my ( $date_due_in_time, $date_due_in_time_as_formatted, $date_due_in_late, $date_due_in_late_as_formatted, $issue_date, $slip, $expected_slip );
232         # Assuming today is not hour = 23 and minute = 59
233         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
234         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
235         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
236         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
237         $issue_date = $yesterday->clone;
238         AddIssue( $borrower, $barcode2, $date_due_in_time, undef, $issue_date );
239
240         # Set timestamps to the same value to avoid a different order
241         Koha::Checkouts->search(
242             { borrowernumber => $borrower->{borrowernumber} }
243         )->update( { timestamp => dt_from_string } );
244
245         $expected_slip = <<EOS;
246 Checked out:
247
248 Title: $title1
249 Barcode: $barcode1
250 Date due: $date_due_in_time_as_formatted
251
252
253 Title: $title2
254 Barcode: $barcode2
255 Date due: $date_due_in_time_as_formatted
256
257
258 Overdues:
259
260 EOS
261         $slip = IssueSlip( $branchcode, $borrowernumber );
262         is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 2 checkouts' )
263             or diag(Dumper(Koha::Checkouts->search({borrowernumber => $borrower->{borrowernumber}})->unblessed));
264
265         AddReturn( $barcode1, $branchcode );
266         AddReturn( $barcode2, $branchcode );
267
268         # Test 2: 1 Overdue
269         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
270         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
271         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
272         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
273         $date_due_in_late = $today->clone->subtract( hours => 1 );
274         $date_due_in_late_as_formatted = output_pref( $date_due_in_late );
275         $issue_date = $yesterday->clone;
276         AddIssue( $borrower, $barcode2, $date_due_in_late, undef, $issue_date );
277
278         $expected_slip = <<EOS;
279 Checked out:
280
281 Title: $title1
282 Barcode: $barcode1
283 Date due: $date_due_in_time_as_formatted
284
285
286 Overdues:
287
288 Title: $title2
289 Barcode: $barcode2
290 Date due: $date_due_in_late_as_formatted
291
292 EOS
293         $slip = IssueSlip( $branchcode, $borrowernumber );
294         is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue' );
295
296         AddReturn( $barcode1, $branchcode );
297         AddReturn( $barcode2, $branchcode );
298     };
299
300 };
301
302 subtest 'Quick slip' => sub {
303     plan tests => 3;
304
305     subtest 'Empty slip' => sub {
306         plan tests => 1;
307         my $slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
308         my $empty_slip = <<EOS;
309 Checked out today:
310
311 EOS
312         is( $slip->{content}, $empty_slip, 'No checked out or overdues return an empty slip, it should return undef (FIXME)' );
313     };
314
315     subtest 'Daily loans' => sub {
316         if ( $today->hour == 23 and $today->minute == 59 ) {
317             plan skip_all => "It's 23:59!";
318         } else {
319             plan tests => 2;
320         }
321         # Test 1: No overdue
322         my $today_daily = $today->clone->set( hour => 23, minute => 59 );
323         my $today_daily_as_formatted = output_pref( $today_daily );
324         my $yesterday_daily = $yesterday->clone->set( hour => 23, minute => 59 );
325         my $yesterday_daily_as_formatted = output_pref( $yesterday_daily );
326
327         my ( $date_due, $issue_date, $slip, $expected_slip );
328         $date_due = $today_daily;
329         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
330         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
331         $date_due = $today_daily;
332         $issue_date = $yesterday_daily;
333         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
334
335         $expected_slip = <<EOS;
336 Checked out today:
337
338 Title: $title1
339 Barcode: $barcode1
340 Date due: $today_daily_as_formatted
341
342 EOS
343         $slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
344         is( $slip->{content}, $expected_slip, 'IssueSlip should return 2 checkouts for today');
345
346         AddReturn( $barcode1, $branchcode );
347         AddReturn( $barcode2, $branchcode );
348
349         # Test 2: 1 Overdue
350         $date_due = $today_daily;
351         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
352         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
353         $date_due = $yesterday_daily;
354         $issue_date = $yesterday_daily;
355         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
356
357         $expected_slip = <<EOS;
358 Checked out today:
359
360 Title: $title1
361 Barcode: $barcode1
362 Date due: $today_daily_as_formatted
363
364 EOS
365         $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
366         is( $slip->{content}, $expected_slip );
367     };
368
369     subtest 'Hourly loans' => sub {
370         plan tests => 2;
371         # Test 1: No overdue
372         my ( $date_due_in_time, $date_due_in_time_as_formatted, $date_due_in_late, $date_due_in_late_as_formatted, $issue_date, $slip, $expected_slip );
373         # Assuming today is not hour = 23 and minute = 59
374         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
375         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
376         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
377         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
378         $issue_date = $yesterday->clone;
379         AddIssue( $borrower, $barcode2, $date_due_in_time, undef, $issue_date );
380
381         $expected_slip = <<EOS;
382 Checked out today:
383
384 Title: $title1
385 Barcode: $barcode1
386 Date due: $date_due_in_time_as_formatted
387
388 EOS
389         $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
390         is( $slip->{content}, $expected_slip );
391
392         AddReturn( $barcode1, $branchcode );
393         AddReturn( $barcode2, $branchcode );
394
395         # Test 2: 1 Overdue
396         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
397         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
398         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
399         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
400         $date_due_in_late = $today->clone->subtract( hours => 1 );
401         $date_due_in_late_as_formatted = output_pref( $date_due_in_late );
402         $issue_date = $yesterday->clone;
403         AddIssue( $borrower, $barcode2, $date_due_in_late, undef, $issue_date );
404
405         $expected_slip = <<EOS;
406 Checked out today:
407
408 Title: $title1
409 Barcode: $barcode1
410 Date due: $date_due_in_time_as_formatted
411
412 EOS
413         $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
414         is( $slip->{content}, $expected_slip );
415
416         AddReturn( $barcode1, $branchcode );
417         AddReturn( $barcode2, $branchcode );
418     };
419
420 };
421
422 subtest 'bad calls' => sub {
423     plan tests => 1;
424     my $slip = IssueSlip();
425     is( $slip, undef, 'IssueSlip should return if no valid borrowernumber is passed' );
426 };
427
428 $schema->storage->txn_rollback;
429