Bug 26384: Fix executable flags
[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' ); # FIXME Must not be hardcoded, tests could fail
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 = $builder->build_sample_item({ biblionumber => $biblionumber1, barcode => $barcode1, %item_infos })->itemnumber;
108
109 $record = MARC::Record->new;
110 $record->append_fields(
111     MARC::Field->new(
112         245, '1', '4',
113             a => $title2,
114     ),
115 );
116 my ($biblionumber2) = AddBiblio( $record, '' );
117 my $itemnumber2 = $builder->build_sample_item({ biblionumber => $biblionumber2, barcode => $barcode2, %item_infos })->itemnumber;
118
119 my $borrowernumber =
120   Koha::Patron->new({ categorycode => $categorycode, branchcode => $branchcode })->store->borrowernumber;
121 my $borrower = Koha::Patrons->find( $borrowernumber )->unblessed;
122
123 my $module = new Test::MockModule('C4::Context');
124 $module->mock( 'userenv', sub { { branch => $branchcode } } );
125
126 my $today = dt_from_string;
127 my $yesterday = dt_from_string->subtract_duration( DateTime::Duration->new( days => 1 ) );
128
129 subtest 'Issue slip' => sub {
130     plan tests => 3;
131
132     subtest 'Empty slip' => sub {
133         plan tests => 1;
134         my $slip = IssueSlip( $branchcode, $borrowernumber );
135         my $empty_slip = <<EOS;
136 Checked out:
137
138
139 Overdues:
140
141 EOS
142         is( $slip->{content}, $empty_slip, 'No checked out or overdues return an empty slip, it should return undef (FIXME)' );
143     };
144
145     subtest 'Daily loans' => sub {
146         if ( $today->hour == 23 and $today->minute == 59 ) {
147             plan skip_all => "It's 23:59!";
148         } else {
149             plan tests => 2;
150         }
151         # Test 1: No overdue
152         my $today_daily = $today->clone->set( hour => 23, minute => 59 );
153         my $today_daily_as_formatted = output_pref( $today_daily );
154         my $yesterday_daily = $yesterday->clone->set( hour => 23, minute => 59 );
155         my $yesterday_daily_as_formatted = output_pref( $yesterday_daily );
156
157         my ( $date_due, $issue_date, $slip, $expected_slip );
158         $date_due = $today_daily;
159         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
160         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
161         $date_due = $today_daily;
162         $issue_date = $yesterday_daily;
163         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
164
165         # Set timestamps to the same value to avoid a different order
166         Koha::Checkouts->search(
167             { borrowernumber => $borrower->{borrowernumber} }
168         )->update( { timestamp => dt_from_string } );
169
170         $expected_slip = <<EOS;
171 Checked out:
172
173 Title: $title1
174 Barcode: $barcode1
175 Date due: $today_daily_as_formatted
176
177
178 Title: $title2
179 Barcode: $barcode2
180 Date due: $today_daily_as_formatted
181
182
183 Overdues:
184
185 EOS
186         $slip = IssueSlip( $branchcode, $borrowernumber );
187         is( $slip->{content}, $expected_slip , 'IssueSlip should return a slip with 2 checkouts');
188
189         AddReturn( $barcode1, $branchcode );
190         AddReturn( $barcode2, $branchcode );
191
192         # Test 2: 1 Overdue
193         $date_due = $today_daily;
194         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
195         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
196         $date_due = $yesterday_daily;
197         $issue_date = $yesterday_daily;
198         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
199
200         $expected_slip = <<EOS;
201 Checked out:
202
203 Title: $title1
204 Barcode: $barcode1
205 Date due: $today_daily_as_formatted
206
207
208 Overdues:
209
210 Title: $title2
211 Barcode: $barcode2
212 Date due: $yesterday_daily_as_formatted
213
214 EOS
215         $slip = IssueSlip( $branchcode, $borrowernumber );
216         is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue');
217
218         AddReturn( $barcode1, $branchcode );
219         AddReturn( $barcode2, $branchcode );
220     };
221
222     subtest 'Hourly loans' => sub {
223         if ( $today->hour == 23 and $today->minute == 59 ) {
224             plan skip_all => "It's 23:59!";
225         } else {
226             plan tests => 2;
227         }
228         # Test 1: No overdue
229         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 );
230         # Assuming today is not hour = 23 and minute = 59
231         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
232         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
233         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
234         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
235         $issue_date = $yesterday->clone;
236         AddIssue( $borrower, $barcode2, $date_due_in_time, undef, $issue_date );
237
238         # Set timestamps to the same value to avoid a different order
239         Koha::Checkouts->search(
240             { borrowernumber => $borrower->{borrowernumber} }
241         )->update( { timestamp => dt_from_string } );
242
243         $expected_slip = <<EOS;
244 Checked out:
245
246 Title: $title1
247 Barcode: $barcode1
248 Date due: $date_due_in_time_as_formatted
249
250
251 Title: $title2
252 Barcode: $barcode2
253 Date due: $date_due_in_time_as_formatted
254
255
256 Overdues:
257
258 EOS
259         $slip = IssueSlip( $branchcode, $borrowernumber );
260         is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 2 checkouts' )
261             or diag(Dumper(Koha::Checkouts->search({borrowernumber => $borrower->{borrowernumber}})->unblessed));
262
263         AddReturn( $barcode1, $branchcode );
264         AddReturn( $barcode2, $branchcode );
265
266         # Test 2: 1 Overdue
267         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
268         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
269         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
270         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
271         $date_due_in_late = $today->clone->subtract( hours => 1 );
272         $date_due_in_late_as_formatted = output_pref( $date_due_in_late );
273         $issue_date = $yesterday->clone;
274         AddIssue( $borrower, $barcode2, $date_due_in_late, undef, $issue_date );
275
276         $expected_slip = <<EOS;
277 Checked out:
278
279 Title: $title1
280 Barcode: $barcode1
281 Date due: $date_due_in_time_as_formatted
282
283
284 Overdues:
285
286 Title: $title2
287 Barcode: $barcode2
288 Date due: $date_due_in_late_as_formatted
289
290 EOS
291         $slip = IssueSlip( $branchcode, $borrowernumber );
292         is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue' );
293
294         AddReturn( $barcode1, $branchcode );
295         AddReturn( $barcode2, $branchcode );
296     };
297
298 };
299
300 subtest 'Quick slip' => sub {
301     plan tests => 3;
302
303     subtest 'Empty slip' => sub {
304         plan tests => 1;
305         my $slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
306         my $empty_slip = <<EOS;
307 Checked out today:
308
309 EOS
310         is( $slip->{content}, $empty_slip, 'No checked out or overdues return an empty slip, it should return undef (FIXME)' );
311     };
312
313     subtest 'Daily loans' => sub {
314         if ( $today->hour == 23 and $today->minute == 59 ) {
315             plan skip_all => "It's 23:59!";
316         } else {
317             plan tests => 2;
318         }
319         # Test 1: No overdue
320         my $today_daily = $today->clone->set( hour => 23, minute => 59 );
321         my $today_daily_as_formatted = output_pref( $today_daily );
322         my $yesterday_daily = $yesterday->clone->set( hour => 23, minute => 59 );
323         my $yesterday_daily_as_formatted = output_pref( $yesterday_daily );
324
325         my ( $date_due, $issue_date, $slip, $expected_slip );
326         $date_due = $today_daily;
327         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
328         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
329         $date_due = $today_daily;
330         $issue_date = $yesterday_daily;
331         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
332
333         $expected_slip = <<EOS;
334 Checked out today:
335
336 Title: $title1
337 Barcode: $barcode1
338 Date due: $today_daily_as_formatted
339
340 EOS
341         $slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
342         is( $slip->{content}, $expected_slip, 'IssueSlip should return 2 checkouts for today');
343
344         AddReturn( $barcode1, $branchcode );
345         AddReturn( $barcode2, $branchcode );
346
347         # Test 2: 1 Overdue
348         $date_due = $today_daily;
349         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
350         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
351         $date_due = $yesterday_daily;
352         $issue_date = $yesterday_daily;
353         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
354
355         $expected_slip = <<EOS;
356 Checked out today:
357
358 Title: $title1
359 Barcode: $barcode1
360 Date due: $today_daily_as_formatted
361
362 EOS
363         $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
364         is( $slip->{content}, $expected_slip );
365     };
366
367     subtest 'Hourly loans' => sub {
368         plan tests => 2;
369         # Test 1: No overdue
370         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 );
371         # Assuming today is not hour = 23 and minute = 59
372         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
373         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
374         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
375         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
376         $issue_date = $yesterday->clone;
377         AddIssue( $borrower, $barcode2, $date_due_in_time, undef, $issue_date );
378
379         $expected_slip = <<EOS;
380 Checked out today:
381
382 Title: $title1
383 Barcode: $barcode1
384 Date due: $date_due_in_time_as_formatted
385
386 EOS
387         $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
388         is( $slip->{content}, $expected_slip );
389
390         AddReturn( $barcode1, $branchcode );
391         AddReturn( $barcode2, $branchcode );
392
393         # Test 2: 1 Overdue
394         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
395         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
396         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
397         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
398         $date_due_in_late = $today->clone->subtract( hours => 1 );
399         $date_due_in_late_as_formatted = output_pref( $date_due_in_late );
400         $issue_date = $yesterday->clone;
401         AddIssue( $borrower, $barcode2, $date_due_in_late, undef, $issue_date );
402
403         $expected_slip = <<EOS;
404 Checked out today:
405
406 Title: $title1
407 Barcode: $barcode1
408 Date due: $date_due_in_time_as_formatted
409
410 EOS
411         $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
412         is( $slip->{content}, $expected_slip );
413
414         AddReturn( $barcode1, $branchcode );
415         AddReturn( $barcode2, $branchcode );
416     };
417
418 };
419
420 subtest 'bad calls' => sub {
421     plan tests => 1;
422     my $slip = IssueSlip();
423     is( $slip, undef, 'IssueSlip should return if no valid borrowernumber is passed' );
424 };
425
426 $schema->storage->txn_rollback;
427