3 # This file is part of Koha.
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.
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.
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>.
21 use Data::Dumper; # REMOVEME with diag
22 use Test::More tests => 3;
24 use Test::MockTime qw( set_fixed_time );
25 use t::lib::TestBuilder;
32 use Koha::DateUtils qw( dt_from_string output_pref );
35 use DateTime::Duration;
39 my $schema = Koha::Database->schema;
40 $schema->storage->txn_begin;
41 my $dbh = C4::Context->dbh;
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|);
50 my $builder = t::lib::TestBuilder->new;
51 set_fixed_time(CORE::time());
53 my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
54 my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
55 my $itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
58 homebranch => $branchcode,
59 holdingbranch => $branchcode,
64 my $slip_content = <<EOS;
67 Title: <<biblio.title>>
68 Barcode: <<items.barcode>>
69 Date due: <<issues.date_due>>
74 Title: <<biblio.title>>
75 Barcode: <<items.barcode>>
76 Date due: <<issues.date_due>>
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);
84 my $quick_slip_content = <<EOS;
87 Title: <<biblio.title>>
88 Barcode: <<items.barcode>>
89 Date due: <<issues.date_due>>
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);
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(
106 my ($biblionumber1) = AddBiblio( $record, '' );
107 my $itemnumber1 = $builder->build_sample_item({ biblionumber => $biblionumber1, barcode => $barcode1, %item_infos })->itemnumber;
109 $record = MARC::Record->new;
110 $record->append_fields(
116 my ($biblionumber2) = AddBiblio( $record, '' );
117 my $itemnumber2 = $builder->build_sample_item({ biblionumber => $biblionumber2, barcode => $barcode2, %item_infos })->itemnumber;
120 Koha::Patron->new({ categorycode => $categorycode, branchcode => $branchcode })->store->borrowernumber;
121 my $borrower = Koha::Patrons->find( $borrowernumber )->unblessed;
123 my $module = Test::MockModule->new('C4::Context');
124 $module->mock( 'userenv', sub { { branch => $branchcode } } );
126 my $today = dt_from_string;
127 my $yesterday = dt_from_string->subtract_duration( DateTime::Duration->new( days => 1 ) );
129 subtest 'Issue slip' => sub {
132 subtest 'Empty slip' => sub {
134 my $slip = IssueSlip( $branchcode, $borrowernumber );
135 my $empty_slip = <<EOS;
142 is( $slip->{content}, $empty_slip, 'No checked out or overdues return an empty slip, it should return undef (FIXME)' );
145 subtest 'Daily loans' => sub {
146 if ( $today->hour == 23 and $today->minute == 59 ) {
147 plan skip_all => "It's 23:59!";
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 );
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 );
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 } );
170 $expected_slip = <<EOS;
175 Date due: $today_daily_as_formatted
180 Date due: $today_daily_as_formatted
186 $slip = IssueSlip( $branchcode, $borrowernumber );
187 is( $slip->{content}, $expected_slip , 'IssueSlip should return a slip with 2 checkouts');
189 AddReturn( $barcode1, $branchcode );
190 AddReturn( $barcode2, $branchcode );
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 );
200 $expected_slip = <<EOS;
205 Date due: $today_daily_as_formatted
212 Date due: $yesterday_daily_as_formatted
215 $slip = IssueSlip( $branchcode, $borrowernumber );
216 is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue');
218 AddReturn( $barcode1, $branchcode );
219 AddReturn( $barcode2, $branchcode );
222 subtest 'Hourly loans' => sub {
223 if ( $today->hour == 23 and $today->minute == 59 ) {
224 plan skip_all => "It's 23:59!";
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 );
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 } );
243 $expected_slip = <<EOS;
248 Date due: $date_due_in_time_as_formatted
253 Date due: $date_due_in_time_as_formatted
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));
263 AddReturn( $barcode1, $branchcode );
264 AddReturn( $barcode2, $branchcode );
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 );
276 $expected_slip = <<EOS;
281 Date due: $date_due_in_time_as_formatted
288 Date due: $date_due_in_late_as_formatted
291 $slip = IssueSlip( $branchcode, $borrowernumber );
292 is( $slip->{content}, $expected_slip, 'IssueSlip should return a slip with 1 checkout and 1 overdue' );
294 AddReturn( $barcode1, $branchcode );
295 AddReturn( $barcode2, $branchcode );
300 subtest 'Quick slip' => sub {
303 subtest 'Empty slip' => sub {
305 my $slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
306 my $empty_slip = <<EOS;
310 is( $slip->{content}, $empty_slip, 'No checked out or overdues return an empty slip, it should return undef (FIXME)' );
313 subtest 'Daily loans' => sub {
314 if ( $today->hour == 23 and $today->minute == 59 ) {
315 plan skip_all => "It's 23:59!";
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 );
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 );
333 $expected_slip = <<EOS;
338 Date due: $today_daily_as_formatted
341 $slip = IssueSlip( $branchcode, $borrowernumber, 'quick slip' );
342 is( $slip->{content}, $expected_slip, 'IssueSlip should return 2 checkouts for today');
344 AddReturn( $barcode1, $branchcode );
345 AddReturn( $barcode2, $branchcode );
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 );
355 $expected_slip = <<EOS;
360 Date due: $today_daily_as_formatted
363 $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
364 is( $slip->{content}, $expected_slip );
367 subtest 'Hourly loans' => sub {
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 );
379 $expected_slip = <<EOS;
384 Date due: $date_due_in_time_as_formatted
387 $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
388 is( $slip->{content}, $expected_slip );
390 AddReturn( $barcode1, $branchcode );
391 AddReturn( $barcode2, $branchcode );
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 );
403 $expected_slip = <<EOS;
408 Date due: $date_due_in_time_as_formatted
411 $slip = IssueSlip( $branchcode, $borrowernumber, 'quickslip' );
412 is( $slip->{content}, $expected_slip );
414 AddReturn( $barcode1, $branchcode );
415 AddReturn( $barcode2, $branchcode );
420 subtest 'bad calls' => sub {
422 my $slip = IssueSlip();
423 is( $slip, undef, 'IssueSlip should return if no valid borrowernumber is passed' );
426 $schema->storage->txn_rollback;