Bug 20531: Set the timestamps to the same value to get a correct order
[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::Biblio;
28 use C4::Items;
29 use C4::Members;
30 use C4::Circulation;
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   AddMember( categorycode => $categorycode, branchcode => $branchcode );
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         plan tests => 2;
149         skip "It's 23:59!", 2 if $today->hour == 23 and $today->minute == 59;
150         # Test 1: No overdue
151         my $today_daily = $today->clone->set( hour => 23, minute => 59 );
152         my $today_daily_as_formatted = output_pref( $today_daily );
153         my $yesterday_daily = $yesterday->clone->set( hour => 23, minute => 59 );
154         my $yesterday_daily_as_formatted = output_pref( $yesterday_daily );
155
156         my ( $date_due, $issue_date, $slip, $expected_slip );
157         $date_due = $today_daily;
158         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
159         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
160         $date_due = $today_daily;
161         $issue_date = $yesterday_daily;
162         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
163
164         $expected_slip = <<EOS;
165 Checked out:
166
167 Title: $title1
168 Barcode: $barcode1
169 Date due: $today_daily_as_formatted
170
171
172 Title: $title2
173 Barcode: $barcode2
174 Date due: $today_daily_as_formatted
175
176
177 Overdues:
178
179 EOS
180         $slip = IssueSlip( $branchcode, $borrowernumber );
181         is( $slip->{content}, $expected_slip , 'IssueSlip should return a slip with 2 checkouts');
182
183         AddReturn( $barcode1, $branchcode );
184         AddReturn( $barcode2, $branchcode );
185
186         # Test 2: 1 Overdue
187         $date_due = $today_daily;
188         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
189         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
190         $date_due = $yesterday_daily;
191         $issue_date = $yesterday_daily;
192         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
193
194         $expected_slip = <<EOS;
195 Checked out:
196
197 Title: $title1
198 Barcode: $barcode1
199 Date due: $today_daily_as_formatted
200
201
202 Overdues:
203
204 Title: $title2
205 Barcode: $barcode2
206 Date due: $yesterday_daily_as_formatted
207
208 EOS
209         $slip = IssueSlip( $branchcode, $borrowernumber );
210         is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue');
211
212         AddReturn( $barcode1, $branchcode );
213         AddReturn( $barcode2, $branchcode );
214     };
215
216     subtest 'Hourly loans' => sub {
217         plan tests => 2;
218         skip "It's 23:59!", 2 if $today->hour == 23 and $today->minute == 59;
219         # Test 1: No overdue
220         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 );
221         # Assuming today is not hour = 23 and minute = 59
222         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
223         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
224         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
225         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
226         $issue_date = $yesterday->clone;
227         AddIssue( $borrower, $barcode2, $date_due_in_time, undef, $issue_date );
228
229         # Set timestamps to the same value to avoid a different order
230         Koha::Checkouts->search(
231             { borrowernumber => $borrower->{borrowernumber} }
232         )->update( { timestamp => dt_from_string } );
233
234         $expected_slip = <<EOS;
235 Checked out:
236
237 Title: $title1
238 Barcode: $barcode1
239 Date due: $date_due_in_time_as_formatted
240
241
242 Title: $title2
243 Barcode: $barcode2
244 Date due: $date_due_in_time_as_formatted
245
246
247 Overdues:
248
249 EOS
250         $slip = IssueSlip( $branchcode, $borrowernumber );
251         is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 2 checkouts' )
252             or diag(Dumper(Koha::Checkouts->search({borrowernumber => $borrower->{borrowernumber}})->unblessed));
253
254         AddReturn( $barcode1, $branchcode );
255         AddReturn( $barcode2, $branchcode );
256
257         # Test 2: 1 Overdue
258         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
259         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
260         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
261         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
262         $date_due_in_late = $today->clone->subtract( hours => 1 );
263         $date_due_in_late_as_formatted = output_pref( $date_due_in_late );
264         $issue_date = $yesterday->clone;
265         AddIssue( $borrower, $barcode2, $date_due_in_late, undef, $issue_date );
266
267         $expected_slip = <<EOS;
268 Checked out:
269
270 Title: $title1
271 Barcode: $barcode1
272 Date due: $date_due_in_time_as_formatted
273
274
275 Overdues:
276
277 Title: $title2
278 Barcode: $barcode2
279 Date due: $date_due_in_late_as_formatted
280
281 EOS
282         $slip = IssueSlip( $branchcode, $borrowernumber );
283         is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue' );
284
285         AddReturn( $barcode1, $branchcode );
286         AddReturn( $barcode2, $branchcode );
287     };
288
289 };
290
291 subtest 'Quick slip' => sub {
292     plan tests => 3;
293
294     subtest 'Empty slip' => sub {
295         plan tests => 1;
296         my $slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
297         my $empty_slip = <<EOS;
298 Checked out today:
299
300 EOS
301         is( $slip->{content}, $empty_slip, 'No checked out or overdues return an empty slip, it should return undef (FIXME)' );
302     };
303
304     subtest 'Daily loans' => sub {
305         plan tests => 2;
306         skip "It's 23:59!", 2 if $today->hour == 23 and $today->minute == 59;
307         # Test 1: No overdue
308         my $today_daily = $today->clone->set( hour => 23, minute => 59 );
309         my $today_daily_as_formatted = output_pref( $today_daily );
310         my $yesterday_daily = $yesterday->clone->set( hour => 23, minute => 59 );
311         my $yesterday_daily_as_formatted = output_pref( $yesterday_daily );
312
313         my ( $date_due, $issue_date, $slip, $expected_slip );
314         $date_due = $today_daily;
315         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
316         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
317         $date_due = $today_daily;
318         $issue_date = $yesterday_daily;
319         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
320
321         $expected_slip = <<EOS;
322 Checked out today:
323
324 Title: $title1
325 Barcode: $barcode1
326 Date due: $today_daily_as_formatted
327
328 EOS
329         $slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
330         is( $slip->{content}, $expected_slip, 'IssueSlip should return 2 checkouts for today');
331
332         AddReturn( $barcode1, $branchcode );
333         AddReturn( $barcode2, $branchcode );
334
335         # Test 2: 1 Overdue
336         $date_due = $today_daily;
337         $issue_date = $today_daily->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
338         AddIssue( $borrower, $barcode1, $date_due, undef, $issue_date );
339         $date_due = $yesterday_daily;
340         $issue_date = $yesterday_daily;
341         AddIssue( $borrower, $barcode2, $date_due, undef, $issue_date );
342
343         $expected_slip = <<EOS;
344 Checked out today:
345
346 Title: $title1
347 Barcode: $barcode1
348 Date due: $today_daily_as_formatted
349
350 EOS
351         $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
352         is( $slip->{content}, $expected_slip );
353     };
354
355     subtest 'Hourly loans' => sub {
356         plan tests => 2;
357         # Test 1: No overdue
358         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 );
359         # Assuming today is not hour = 23 and minute = 59
360         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
361         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
362         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
363         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
364         $issue_date = $yesterday->clone;
365         AddIssue( $borrower, $barcode2, $date_due_in_time, undef, $issue_date );
366
367         $expected_slip = <<EOS;
368 Checked out today:
369
370 Title: $title1
371 Barcode: $barcode1
372 Date due: $date_due_in_time_as_formatted
373
374 EOS
375         $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
376         is( $slip->{content}, $expected_slip );
377
378         AddReturn( $barcode1, $branchcode );
379         AddReturn( $barcode2, $branchcode );
380
381         # Test 2: 1 Overdue
382         $date_due_in_time = $today->clone->set(hour => ($today->hour < 23 ? $today->hour + 1 : 23), minute => 59);
383         $date_due_in_time_as_formatted = output_pref( $date_due_in_time );
384         $issue_date = $date_due_in_time->clone->subtract_duration( DateTime::Duration->new( minutes => 1 ) );
385         AddIssue( $borrower, $barcode1, $date_due_in_time, undef, $issue_date );
386         $date_due_in_late = $today->clone->subtract( hours => 1 );
387         $date_due_in_late_as_formatted = output_pref( $date_due_in_late );
388         $issue_date = $yesterday->clone;
389         AddIssue( $borrower, $barcode2, $date_due_in_late, undef, $issue_date );
390
391         $expected_slip = <<EOS;
392 Checked out today:
393
394 Title: $title1
395 Barcode: $barcode1
396 Date due: $date_due_in_time_as_formatted
397
398 EOS
399         $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
400         is( $slip->{content}, $expected_slip );
401
402         AddReturn( $barcode1, $branchcode );
403         AddReturn( $barcode2, $branchcode );
404     };
405
406 };
407
408 subtest 'bad calls' => sub {
409     plan tests => 1;
410     my $slip = IssueSlip();
411     is( $slip, undef, 'IssueSlip should return if no valid borrowernumber is passed' );
412 };
413
414 $schema->storage->txn_rollback;
415