3 # This file is part of Koha.
5 # Copyright (C) 2016 ByWater Solutions
6 # Copyright (C) 2017 Koha Development Team
8 # Koha is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use Test
::More tests
=> 19;
28 use t
::lib
::TestBuilder
;
37 use Koha
::ArticleRequests
;
44 use Koha
::Subscription
;
47 use Koha
::Notice
::Messages
;
48 use Koha
::Notice
::Templates
;
49 use Koha
::Patron
::Modification
;
51 my $schema = Koha
::Database
->schema;
52 $schema->storage->txn_begin();
54 my $builder = t
::lib
::TestBuilder
->new();
56 my $dbh = C4
::Context
->dbh;
57 $dbh->{RaiseError
} = 1;
59 $dbh->do(q
|DELETE FROM letter
|);
61 my $now_value = DateTime
->now();
62 my $mocked_datetime = Test
::MockModule
->new('DateTime');
63 $mocked_datetime->mock( 'now', sub { return $now_value->clone; } );
65 my $library = $builder->build( { source
=> 'Branch' } );
66 my $patron = $builder->build( { source
=> 'Borrower' } );
67 my $patron2 = $builder->build( { source
=> 'Borrower' } );
69 my $biblio = Koha
::Biblio
->new(
71 title
=> 'Test Biblio'
75 my $biblioitem = Koha
::Biblioitem
->new(
77 biblionumber
=> $biblio->id()
81 my $item = Koha
::Item
->new(
83 biblionumber
=> $biblio->id(),
84 biblioitemnumber
=> $biblioitem->id()
88 my $hold = Koha
::Hold
->new(
90 borrowernumber
=> $patron->{borrowernumber
},
91 biblionumber
=> $biblio->id()
95 my $news = Koha
::NewsItem
->new({ title
=> 'a news title', content
=> 'a news content'})->store();
96 my $serial = Koha
::Serial
->new()->store();
97 my $subscription = Koha
::Subscription
->new()->store();
98 my $suggestion = Koha
::Suggestion
->new()->store();
99 my $checkout = Koha
::Checkout
->new( { itemnumber
=> $item->id() } )->store();
100 my $modification = Koha
::Patron
::Modification
->new( { verification_token
=> "TEST", changed_fields
=> 'firstname,surname' } )->store();
105 $dbh->prepare(q{INSERT INTO letter (module, code, name, title, content) VALUES ('test',?,'Test','Test',?)});
107 $sth->execute( "TEST_PATRON", "[% borrower.id %]" );
108 $prepared_letter = GetPreparedLetter
(
111 letter_code
=> 'TEST_PATRON',
113 borrowers
=> $patron->{borrowernumber
},
117 is
( $prepared_letter->{content
}, $patron->{borrowernumber
}, 'Patron object used correctly with scalar' );
119 $prepared_letter = GetPreparedLetter
(
122 letter_code
=> 'TEST_PATRON',
124 borrowers
=> $patron,
128 is
( $prepared_letter->{content
}, $patron->{borrowernumber
}, 'Patron object used correctly with hashref' );
130 $prepared_letter = GetPreparedLetter
(
133 letter_code
=> 'TEST_PATRON',
135 borrowers
=> [ $patron->{borrowernumber
} ],
139 is
( $prepared_letter->{content
}, $patron->{borrowernumber
}, 'Patron object used correctly with arrayref' );
141 $sth->execute( "TEST_BIBLIO", "[% biblio.id %]" );
142 $prepared_letter = GetPreparedLetter
(
145 letter_code
=> 'TEST_BIBLIO',
147 biblio
=> $biblio->id(),
151 is
( $prepared_letter->{content
}, $biblio->id, 'Biblio object used correctly' );
153 $sth->execute( "TEST_LIBRARY", "[% branch.id %]" );
154 $prepared_letter = GetPreparedLetter
(
157 letter_code
=> 'TEST_LIBRARY',
159 branches
=> $library->{branchcode
}
163 is
( $prepared_letter->{content
}, $library->{branchcode
}, 'Library object used correctly' );
165 $sth->execute( "TEST_ITEM", "[% item.id %]" );
166 $prepared_letter = GetPreparedLetter
(
169 letter_code
=> 'TEST_ITEM',
175 is
( $prepared_letter->{content
}, $item->id(), 'Item object used correctly' );
177 $sth->execute( "TEST_NEWS", "[% news.id %]" );
178 $prepared_letter = GetPreparedLetter
(
181 letter_code
=> 'TEST_NEWS',
183 opac_news
=> $news->id()
187 is
( $prepared_letter->{content
}, $news->id(), 'News object used correctly' );
189 $sth->execute( "TEST_HOLD", "[% hold.id %]" );
190 $prepared_letter = GetPreparedLetter
(
193 letter_code
=> 'TEST_HOLD',
195 reserves
=> { borrowernumber
=> $patron->{borrowernumber
}, biblionumber
=> $biblio->id() },
199 is
( $prepared_letter->{content
}, $hold->id(), 'Hold object used correctly' );
202 $prepared_letter = GetPreparedLetter
(
205 letter_code
=> 'TEST_HOLD',
207 reserves
=> [ $patron->{borrowernumber
}, $biblio->id() ],
213 like
( $croak, qr{^Multiple foreign keys \(table reserves\) should be passed using an hashref.*}, "GetPreparedLetter should not be called with arrayref for multiple FK" );
216 $prepared_letter = GetPreparedLetter
(
219 letter_code
=> 'TEST_HOLD',
221 'branches' => $library,
222 'borrowers' => $patron,
223 'biblio' => $biblio->id,
224 'biblioitems' => $biblioitem->id,
225 'reserves' => $hold->unblessed,
226 'items' => $hold->itemnumber,
230 is
( $prepared_letter->{content
}, $hold->id(), 'Hold object used correctly' );
232 $sth->execute( "TEST_SERIAL", "[% serial.id %]" );
233 $prepared_letter = GetPreparedLetter
(
236 letter_code
=> 'TEST_SERIAL',
238 serial
=> $serial->id()
242 is
( $prepared_letter->{content
}, $serial->id(), 'Serial object used correctly' );
244 $sth->execute( "TEST_SUBSCRIPTION", "[% subscription.id %]" );
245 $prepared_letter = GetPreparedLetter
(
248 letter_code
=> 'TEST_SUBSCRIPTION',
250 subscription
=> $subscription->id()
254 is
( $prepared_letter->{content
}, $subscription->id(), 'Subscription object used correctly' );
256 $sth->execute( "TEST_SUGGESTION", "[% suggestion.id %]" );
257 $prepared_letter = GetPreparedLetter
(
260 letter_code
=> 'TEST_SUGGESTION',
262 suggestions
=> $suggestion->id()
266 is
( $prepared_letter->{content
}, $suggestion->id(), 'Suggestion object used correctly' );
268 $sth->execute( "TEST_ISSUE", "[% checkout.id %]" );
269 $prepared_letter = GetPreparedLetter
(
272 letter_code
=> 'TEST_ISSUE',
274 issues
=> $item->id()
278 is
( $prepared_letter->{content
}, $checkout->id(), 'Checkout object used correctly' );
280 $sth->execute( "TEST_MODIFICATION", "[% patron_modification.id %]" );
281 $prepared_letter = GetPreparedLetter
(
284 letter_code
=> 'TEST_MODIFICATION',
286 borrower_modifications
=> $modification->verification_token,
290 is
( $prepared_letter->{content
}, $modification->id(), 'Patron modification object used correctly' );
292 subtest
'regression tests' => sub {
295 my $library = $builder->build( { source
=> 'Branch' } );
296 my $patron = $builder->build( { source
=> 'Borrower' } );
297 my $biblio1 = Koha
::Biblio
->new({title
=> 'Test Biblio 1', author
=> 'An author', })->store->unblessed;
298 my $biblioitem1 = Koha
::Biblioitem
->new({biblionumber
=> $biblio1->{biblionumber
}})->store()->unblessed;
299 my $item1 = Koha
::Item
->new(
301 biblionumber
=> $biblio1->{biblionumber
},
302 biblioitemnumber
=> $biblioitem1->{biblioitemnumber
},
303 barcode
=> 'a_t_barcode',
304 homebranch
=> $library->{branchcode
},
305 holdingbranch
=> $library->{branchcode
},
307 itemcallnumber
=> 'itemcallnumber1',
310 my $biblio2 = Koha
::Biblio
->new({title
=> 'Test Biblio 2'})->store->unblessed;
311 my $biblioitem2 = Koha
::Biblioitem
->new({biblionumber
=> $biblio2->{biblionumber
}})->store()->unblessed;
312 my $item2 = Koha
::Item
->new(
314 biblionumber
=> $biblio2->{biblionumber
},
315 biblioitemnumber
=> $biblioitem2->{biblioitemnumber
},
316 barcode
=> 'another_t_barcode',
317 homebranch
=> $library->{branchcode
},
318 holdingbranch
=> $library->{branchcode
},
320 itemcallnumber
=> 'itemcallnumber2',
323 my $biblio3 = Koha
::Biblio
->new({title
=> 'Test Biblio 3'})->store->unblessed;
324 my $biblioitem3 = Koha
::Biblioitem
->new({biblionumber
=> $biblio3->{biblionumber
}})->store()->unblessed;
325 my $item3 = Koha
::Item
->new(
327 biblionumber
=> $biblio3->{biblionumber
},
328 biblioitemnumber
=> $biblioitem3->{biblioitemnumber
},
329 barcode
=> 'another_t_barcode_3',
330 homebranch
=> $library->{branchcode
},
331 holdingbranch
=> $library->{branchcode
},
333 itemcallnumber
=> 'itemcallnumber3',
337 t
::lib
::Mocks
::mock_userenv
({ branchcode
=> $library->{branchcode
} });
339 subtest
'ACQ_NOTIF_ON_RECEIV ' => sub {
341 my $code = 'ACQ_NOTIF_ON_RECEIV';
342 my $branchcode = $library->{branchcode
};
343 my $order = $builder->build({ source
=> 'Aqorder' });
346 Dear
<<borrowers.firstname>> <<borrowers.surname
>>,
347 The order
<<aqorders.ordernumber>> (<<biblio.title>>) has been received.
350 my $params = { code => $code, branchcode => $branchcode, tables => { branches => $library, borrowers => $patron, biblio => $biblio1, aqorders
=> $order } };
351 my $letter = process_letter
( { template
=> $template, %$params });
353 Dear
[% borrower
.firstname
%] [% borrower
.surname
%],
354 The order
[% order
.ordernumber
%] ([% biblio
.title
%]) has been received
.
357 my $tt_letter = process_letter
( { template
=> $tt_template, %$params });
359 is
( $tt_letter->{content
}, $letter->{content
}, 'Verified letter content' );
362 subtest
'AR_*' => sub {
364 my $code = 'AR_CANCELED';
365 my $branchcode = $library->{branchcode
};
368 <<borrowers.firstname>> <<borrowers.surname
>> (<<borrowers.cardnumber>>)
370 Your request for an article from <<biblio.title>> (<<items.barcode>>) has been canceled for the following reason:
372 <<article_requests.notes>>
375 Title: <<article_requests.title>>
376 Author: <<article_requests.author>>
377 Volume: <<article_requests.volume>>
378 Issue: <<article_requests.issue>>
379 Date: <<article_requests.date>>
380 Pages: <<article_requests.pages>>
381 Chapters: <<article_requests.chapters>>
382 Notes: <<article_requests.patron_notes>>
384 reset_template( { template => $template, code => $code, module => 'circulation' } );
385 my $article_request = $builder->build({ source => 'ArticleRequest' });
386 Koha::ArticleRequests->find( $article_request->{id} )->cancel;
387 my $letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
390 [% borrower.firstname %] [% borrower.surname %] ([% borrower.cardnumber %])
392 Your request for an article from
[% biblio
.title
%] ([% item
.barcode
%]) has been canceled
for the following reason
:
394 [% article_request
.notes
%]
397 Title
: [% article_request
.title
%]
398 Author
: [% article_request
.author
%]
399 Volume
: [% article_request
.volume
%]
400 Issue
: [% article_request
.issue
%]
401 Date
: [% article_request
.date
%]
402 Pages
: [% article_request
.pages
%]
403 Chapters
: [% article_request
.chapters
%]
404 Notes
: [% article_request
.patron_notes
%]
406 reset_template
( { template
=> $tt_template, code
=> $code, module
=> 'circulation' } );
407 Koha
::ArticleRequests
->find( $article_request->{id
} )->cancel;
408 my $tt_letter = Koha
::Notice
::Messages
->search( {}, { order_by
=> { -desc
=> 'message_id' } } )->next;
409 is
( $tt_letter->content, $letter->content, 'Compare AR_* notices' );
410 isnt
( $tt_letter->message_id, $letter->message_id, 'Comparing AR_* notices should compare 2 different messages' );
413 subtest
'CHECKOUT+CHECKIN' => sub {
416 my $checkout_code = 'CHECKOUT';
417 my $checkin_code = 'CHECKIN';
419 my $dbh = C4
::Context
->dbh;
420 # Enable notification for CHECKOUT - Things are hardcoded here but should work with default data
421 $dbh->do(q
|INSERT INTO borrower_message_preferences
( borrowernumber
, message_attribute_id
) VALUES
( ?
, ?
)|, undef, $patron->{borrowernumber
}, 6 );
422 my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
423 $dbh->do(q
|INSERT INTO borrower_message_transport_preferences
( borrower_message_preference_id
, message_transport_type
) VALUES
( ?
, ?
)|, undef, $borrower_message_preference_id, 'email' );
424 # Enable notification for CHECKIN - Things are hardcoded here but should work with default data
425 $dbh->do(q
|INSERT INTO borrower_message_preferences
( borrowernumber
, message_attribute_id
) VALUES
( ?
, ?
)|, undef, $patron->{borrowernumber
}, 5 );
426 $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
427 $dbh->do(q
|INSERT INTO borrower_message_transport_preferences
( borrower_message_preference_id
, message_transport_type
) VALUES
( ?
, ?
)|, undef, $borrower_message_preference_id, 'email' );
430 my $checkout_template = q
|
431 The following items have been checked out
:
435 Thank you for visiting <<branches.branchname>>.
437 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
438 my $checkin_template = q[
439 The following items have been checked out:
441 <<biblio.title
>> was due on
<<old_issues.date_due | dateonly>>
443 Thank you for visiting <<branches.branchname>>.
445 reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
447 C4::Circulation::AddIssue( $patron, $item1->{barcode} );
448 my $first_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
449 C4::Circulation::AddIssue( $patron, $item2->{barcode} );
450 my $second_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
452 AddReturn( $item1->{barcode} );
453 my $first_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
454 AddReturn( $item2->{barcode} );
455 my $second_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
457 Koha::Notice::Messages->delete;
460 $checkout_template = q|
461 The following items have been checked out:
465 Thank you for visiting [% branch.branchname %].
467 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
468 $checkin_template = q[
469 The following items have been checked out:
471 [% biblio.title %] was due on [% old_checkout.date_due | $KohaDates %]
473 Thank you for visiting [% branch.branchname %].
475 reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
477 C4::Circulation::AddIssue( $patron, $item1->{barcode} );
478 my $first_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
479 C4::Circulation::AddIssue( $patron, $item2->{barcode} );
480 my $second_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
482 AddReturn( $item1->{barcode} );
483 my $first_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
484 AddReturn( $item2->{barcode} );
485 my $second_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
487 is( $first_checkout_tt_letter->content, $first_checkout_letter->content, 'Verify first checkout letter' );
488 is( $second_checkout_tt_letter->content, $second_checkout_letter->content, 'Verify second checkout letter' );
489 is( $first_checkin_tt_letter->content, $first_checkin_letter->content, 'Verify first checkin letter' );
490 is( $second_checkin_tt_letter->content, $second_checkin_letter->content, 'Verify second checkin letter' );
494 subtest 'DUEDGST|count' => sub {
497 my $code = 'DUEDGST';
499 my $dbh = C4::Context->dbh;
500 # Enable notification for DUEDGST - Things are hardcoded here but should work with default data
501 $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 1 );
502 my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
503 $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
507 substitute => { count => 42 },
511 You have <<count>> items due
513 my $letter = process_letter( { template => $template, %$params });
516 You have [% count %] items due
518 my $tt_letter = process_letter
( { template
=> $tt_template, %$params });
519 is
( $tt_letter->{content
}, $letter->{content
}, );
522 subtest
'HOLD_SLIP|dates|today' => sub {
525 my $code = 'HOLD_SLIP';
527 C4
::Reserves
::AddReserve
( $library->{branchcode
}, $patron->{borrowernumber
}, $biblio1->{biblionumber
}, undef, undef, undef, undef, "a note", undef, $item1->{itemnumber
}, 'W' );
528 C4
::Reserves
::AddReserve
( $library->{branchcode
}, $patron->{borrowernumber
}, $biblio2->{biblionumber
}, undef, undef, undef, undef, "another note", undef, $item2->{itemnumber
} );
530 my $template = <<EOF;
531 <h5>Date: <<today>></h5>
533 <h3> Transfer to/Hold in <<branches.branchname>></h3>
535 <h3><<borrowers.surname>>, <<borrowers.firstname>></h3>
538 <li><<borrowers.cardnumber>></li>
539 <li><<borrowers.phone>></li>
540 <li> <<borrowers.address>><br />
541 <<borrowers.address2>><br />
542 <<borrowers.city>> <<borrowers.zipcode>>
544 <li><<borrowers.email>></li>
547 <h3>ITEM ON HOLD</h3>
548 <h4><<biblio.title>></h4>
549 <h5><<biblio.author>></h5>
551 <li><<items.barcode>></li>
552 <li><<items.itemcallnumber>></li>
553 <li><<reserves.waitingdate>></li>
556 <pre><<reserves.reservenotes>></pre>
560 reset_template( { template => $template, code => $code, module => 'circulation' } );
561 my $letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio1->{biblionumber} } );
562 my $letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio2->{biblionumber} } );
564 my $tt_template = <<EOF;
565 <h5
>Date
: [% today
| \
$KohaDates with_hours
=> 1 %]</h5
>
567 <h3
> Transfer to
/Hold in [% branch.branchname %]</h3
>
569 <h3
>[% borrower
.surname
%], [% borrower
.firstname
%]</h3
>
572 <li
>[% borrower
.cardnumber
%]</li
>
573 <li
>[% borrower
.phone
%]</li
>
574 <li
> [% borrower
.address
%]<br
/>
575 [% borrower
.address2
%]<br
/>
576 [% borrower
.city
%] [% borrower
.zipcode
%]
578 <li
>[% borrower
.email
%]</li
>
581 <h3
>ITEM ON HOLD
</h3
>
582 <h4
>[% biblio
.title
%]</h4
>
583 <h5
>[% biblio
.author
%]</h5
>
585 <li
>[% item
.barcode
%]</li
>
586 <li
>[% item
.itemcallnumber
%]</li
>
587 <li
>[% hold
.waitingdate
| \
$KohaDates %]</li
>
590 <pre
>[% hold
.reservenotes
%]</pre
>
594 reset_template
( { template
=> $tt_template, code
=> $code, module
=> 'circulation' } );
595 my $tt_letter_for_item1 = C4
::Reserves
::ReserveSlip
( { branchcode
=> $library->{branchcode
}, borrowernumber
=> $patron->{borrowernumber
}, biblionumber
=> $biblio1->{biblionumber
} } );
596 my $tt_letter_for_item2 = C4
::Reserves
::ReserveSlip
( { branchcode
=> $library->{branchcode
}, borrowernumber
=> $patron->{borrowernumber
}, biblionumber
=> $biblio2->{biblionumber
} } );
598 is
( $tt_letter_for_item1->{content
}, $letter_for_item1->{content
}, );
599 is
( $tt_letter_for_item2->{content
}, $letter_for_item2->{content
}, );
602 subtest
'ISSUESLIP|checkedout|repeat' => sub {
605 my $code = 'ISSUESLIP';
606 my $now = dt_from_string
;
607 my $one_minute_ago = dt_from_string
->subtract( minutes
=> 1 );
609 my $branchcode = $library->{branchcode
};
612 my $news_item = Koha
::NewsItem
->new({ branchcode
=> $branchcode, title
=> "A wonderful news", content
=> "This is the wonderful news." })->store;
615 my $template = <<EOF;
616 <h3><<branches.branchname>></h3>
617 Checked out to <<borrowers.title>> <<borrowers.firstname>> <<borrowers.initials>> <<borrowers.surname>> <br />
618 (<<borrowers.cardnumber>>) <br />
625 <<biblio.title>> <br />
626 Barcode: <<items.barcode>><br />
627 Date due: <<issues.date_due | dateonly>><br />
634 <<biblio.title>> <br />
635 Barcode: <<items.barcode>><br />
636 Date due: <<issues.date_due | dateonly>><br />
642 <h4 style="text-align: center; font-style:italic;">News</h4>
644 <div class="newsitem">
645 <h5 style="margin-bottom: 1px; margin-top: 1px"><b><<opac_news.title>></b></h5>
646 <p style="margin-bottom: 1px; margin-top: 1px"><<opac_news.content>></p>
647 <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on <<opac_news.timestamp>></p>
653 reset_template( { template => $template, code => $code, module => 'circulation' } );
655 my $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
656 $checkout->set( { timestamp => $now, issuedate => $one_minute_ago } )->store;
657 my $first_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
659 $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
660 $checkout->set( { timestamp => $now, issuedate => $now } )->store;
661 my $yesterday = dt_from_string->subtract( days => 1 );
662 C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
663 my $second_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
666 AddReturn( $item1->{barcode} );
667 AddReturn( $item2->{barcode} );
668 AddReturn( $item3->{barcode} );
671 my $tt_template = <<EOF;
672 <h3
>[% branch
.branchname
%]</h3
>
673 Checked out to
[% borrower
.title
%] [% borrower
.firstname
%] [% borrower
.initials
%] [% borrower
.surname
%] <br
/>
674 ([% borrower
.cardnumber
%]) <br
/>
676 [% today
| \
$KohaDates with_hours
=> 1 %]<br
/>
679 [% FOREACH checkout IN checkouts
%]
680 [%~ SET item
= checkout
.item
%]
681 [%~ SET biblio
= checkout
.item
.biblio
%]
683 [% biblio
.title
%] <br
/>
684 Barcode
: [% item
.barcode
%]<br
/>
685 Date due
: [% checkout
.date_due
| \
$KohaDates %]<br
/>
690 [% FOREACH overdue IN overdues
%]
691 [%~ SET item
= overdue
.item
%]
692 [%~ SET biblio
= overdue
.item
.biblio
%]
694 [% biblio
.title
%] <br
/>
695 Barcode
: [% item
.barcode
%]<br
/>
696 Date due
: [% overdue
.date_due
| \
$KohaDates %]<br
/>
702 <h4 style
="text-align: center; font-style:italic;">News
</h4
>
703 [% FOREACH n IN news
%]
704 <div
class="newsitem">
705 <h5 style
="margin-bottom: 1px; margin-top: 1px"><b
>[% n
.title
%]</b></h5
>
706 <p style
="margin-bottom: 1px; margin-top: 1px">[% n
.content
%]</p
>
707 <p
class="newsfooter" style
="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on
[% n
.timestamp
| \
$KohaDates %]</p
>
713 reset_template
( { template
=> $tt_template, code
=> $code, module
=> 'circulation' } );
715 $checkout = C4
::Circulation
::AddIssue
( $patron, $item1->{barcode
} ); # Add a first checkout
716 $checkout->set( { timestamp
=> $now, issuedate
=> $one_minute_ago } )->store;
717 my $first_tt_slip = C4
::Members
::IssueSlip
( $branchcode, $patron->{borrowernumber
} );
719 $checkout = C4
::Circulation
::AddIssue
( $patron, $item2->{barcode
} ); # Add a second checkout
720 $checkout->set( { timestamp
=> $now, issuedate
=> $now } )->store;
721 C4
::Circulation
::AddIssue
( $patron, $item3->{barcode
}, $yesterday ); # Add an overdue
722 my $second_tt_slip = C4
::Members
::IssueSlip
( $branchcode, $patron->{borrowernumber
} );
724 # There is too many line breaks generated by the historic syntax
725 $second_slip->{content
} =~ s
|</p>\n\n\n<p>|</p>\n\n<p
>|s
;
727 is
( $first_tt_slip->{content
}, $first_slip->{content
}, );
728 is
( $second_tt_slip->{content
}, $second_slip->{content
}, );
731 AddReturn
( $item1->{barcode
} );
732 AddReturn
( $item2->{barcode
} );
733 AddReturn
( $item3->{barcode
} );
736 subtest
'ODUE|items.content|item' => sub {
741 my $branchcode = $library->{branchcode
};
744 # FIXME items.fine does not work with TT notices
746 # <item> should contain Fine: <<items.fine>></item>
747 my $template = <<EOF;
748 Dear <<borrowers.firstname>> <<borrowers.surname>>,
750 According to our current records, you have items that are overdue.Your library does not charge late fines, but please return or renew them at the branch below as soon as possible.
752 <<branches.branchname>>
753 <<branches.branchaddress1>>
754 <<branches.branchaddress2>> <<branches.branchaddress3>>
755 Phone: <<branches.branchphone>>
756 Fax: <<branches.branchfax>>
757 Email: <<branches.branchemail>>
759 If you have registered a password with the library, and you have a renewal available, you may renew online. If an item becomes more than 30 days overdue, you will be unable to use your library card until the item is returned.
761 The following item(s) is/are currently overdue:
763 <item>"<<biblio.title>>" by <<biblio.author>>, <<items.itemcallnumber>>, Barcode: <<items.barcode>></item>
767 Thank-you for your prompt attention to this matter.
769 <<branches.branchname>> Staff
772 reset_template( { template => $template, code => $code, module => 'circulation' } );
774 my $yesterday = dt_from_string->subtract( days => 1 );
775 my $two_days_ago = dt_from_string->subtract( days => 2 );
776 my $issue1 = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
777 my $issue2 = C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
778 my $issue3 = C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
779 $issue1 = $issue1->unblessed;
780 $issue2 = $issue2->unblessed;
781 $issue3 = $issue3->unblessed;
784 my @item_fields = qw( date_due title barcode author itemnumber );
785 my $items_content = C4
::Letters
::get_item_content
( { item
=> { %$item1, %$biblio1, %$issue1 }, item_content_fields
=> \
@item_fields, dateonly
=> 1 } );
786 $items_content .= C4
::Letters
::get_item_content
( { item
=> { %$item2, %$biblio2, %$issue2 }, item_content_fields
=> \
@item_fields, dateonly
=> 1 } );
787 $items_content .= C4
::Letters
::get_item_content
( { item
=> { %$item3, %$biblio3, %$issue3 }, item_content_fields
=> \
@item_fields, dateonly
=> 1 } );
789 my @items = ( $item1, $item2, $item3 );
790 my $letter = C4
::Overdues
::parse_overdues_letter
(
792 letter_code
=> $code,
793 borrowernumber
=> $patron->{borrowernumber
},
794 branchcode
=> $library->{branchcode
},
797 bib
=> $library->{branchname
},
798 'items.content' => $items_content,
799 count
=> scalar( @items ),
800 message_transport_type
=> 'email',
806 AddReturn
( $item1->{barcode
} );
807 AddReturn
( $item2->{barcode
} );
808 AddReturn
( $item3->{barcode
} );
812 my $tt_template = <<EOF;
813 Dear [% borrower.firstname %] [% borrower.surname %],
815 According to our current records, you have items that are overdue.Your library does not charge late fines, but please return or renew them at the branch below as soon as possible.
817 [% branch.branchname %]
818 [% branch.branchaddress1 %]
819 [% branch.branchaddress2 %] [% branch.branchaddress3 %]
820 Phone: [% branch.branchphone %]
821 Fax: [% branch.branchfax %]
822 Email: [% branch.branchemail %]
824 If you have registered a password with the library, and you have a renewal available, you may renew online. If an item becomes more than 30 days overdue, you will be unable to use your library card until the item is returned.
826 The following item(s) is/are currently overdue:
828 [% FOREACH overdue IN overdues %]
829 [%~ SET item = overdue.item ~%]
830 "[% item.biblio.title %]" by [% item.biblio.author %], [% item.itemcallnumber %], Barcode: [% item.barcode %]
832 [% FOREACH overdue IN overdues %]
833 [%~ SET item = overdue.item ~%]
834 [% overdue.date_due | \$KohaDates %]\t[% item.biblio.title %]\t[% item.barcode %]\t[% item.biblio.author %]\t[% item.itemnumber %]
837 Thank-you for your prompt attention to this matter.
839 [% branch.branchname %] Staff
842 reset_template
( { template
=> $tt_template, code
=> $code, module
=> 'circulation' } );
844 C4
::Circulation
::AddIssue
( $patron, $item1->{barcode
} ); # Add a first checkout
845 C4
::Circulation
::AddIssue
( $patron, $item2->{barcode
}, $yesterday ); # Add an first overdue
846 C4
::Circulation
::AddIssue
( $patron, $item3->{barcode
}, $two_days_ago ); # Add an second overdue
848 my $tt_letter = C4
::Overdues
::parse_overdues_letter
(
850 letter_code
=> $code,
851 borrowernumber
=> $patron->{borrowernumber
},
852 branchcode
=> $library->{branchcode
},
855 bib
=> $library->{branchname
},
856 'items.content' => $items_content,
857 count
=> scalar( @items ),
858 message_transport_type
=> 'email',
863 is
( $tt_letter->{content
}, $letter->{content
}, );
866 subtest
'Bug 19743 - Header and Footer should be updated on each item for checkin / checkout / renewal notices' => sub {
869 my $checkout_code = 'CHECKOUT';
870 my $checkin_code = 'CHECKIN';
872 my $dbh = C4
::Context
->dbh;
873 $dbh->do("DELETE FROM letter");
874 $dbh->do("DELETE FROM issues");
875 $dbh->do("DELETE FROM message_queue");
877 # Enable notification for CHECKOUT - Things are hardcoded here but should work with default data
878 $dbh->do(q
|INSERT INTO borrower_message_preferences
( borrowernumber
, message_attribute_id
) VALUES
( ?
, ?
)|, undef, $patron->{borrowernumber
}, 6 );
879 my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
880 $dbh->do(q
|INSERT INTO borrower_message_transport_preferences
( borrower_message_preference_id
, message_transport_type
) VALUES
( ?
, ?
)|, undef, $borrower_message_preference_id, 'email' );
881 # Enable notification for CHECKIN - Things are hardcoded here but should work with default data
882 $dbh->do(q
|INSERT INTO borrower_message_preferences
( borrowernumber
, message_attribute_id
) VALUES
( ?
, ?
)|, undef, $patron->{borrowernumber
}, 5 );
883 $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
884 $dbh->do(q
|INSERT INTO borrower_message_transport_preferences
( borrower_message_preference_id
, message_transport_type
) VALUES
( ?
, ?
)|, undef, $borrower_message_preference_id, 'email' );
886 my $checkout_template = q
|
887 <<branches.branchname>>
891 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
892 my $checkin_template = q[
893 <<branches.branchname
>>
897 reset_template
( { template
=> $checkin_template, code
=> $checkin_code, module
=> 'circulation' } );
899 my $issue = C4
::Circulation
::AddIssue
( $patron, $item1->{barcode
} );
900 my $first_checkout_letter = Koha
::Notice
::Messages
->search( {}, { order_by
=> { -desc
=> 'message_id' } } )->next;
902 my $library_object = Koha
::Libraries
->find( $issue->branchcode );
903 my $old_branchname = $library_object->branchname;
904 my $new_branchname = "Kyle M Hall Memorial Library";
906 # Change branch name for second checkout notice
907 $library_object->branchname($new_branchname);
908 $library_object->store();
910 C4
::Circulation
::AddIssue
( $patron, $item2->{barcode
} );
911 my $second_checkout_letter = Koha
::Notice
::Messages
->search( {}, { order_by
=> { -desc
=> 'message_id' } } )->next;
913 # Restore old name for first checkin notice
914 $library_object->branchname( $old_branchname );
915 $library_object->store();
917 AddReturn
( $item1->{barcode
} );
918 my $first_checkin_letter = Koha
::Notice
::Messages
->search( {}, { order_by
=> { -desc
=> 'message_id' } } )->next;
920 # Change branch name for second checkin notice
921 $library_object->branchname($new_branchname);
922 $library_object->store();
924 AddReturn
( $item2->{barcode
} );
925 my $second_checkin_letter = Koha
::Notice
::Messages
->search( {}, { order_by
=> { -desc
=> 'message_id' } } )->next;
927 # Restore old name for first TT checkout notice
928 $library_object->branchname( $old_branchname );
929 $library_object->store();
931 Koha
::Notice
::Messages
->delete;
934 $checkout_template = q
|
935 [% branch
.branchname
%]
939 reset_template
( { template
=> $checkout_template, code
=> $checkout_code, module
=> 'circulation' } );
940 $checkin_template = q
[
941 [% branch
.branchname
%]
945 reset_template
( { template
=> $checkin_template, code
=> $checkin_code, module
=> 'circulation' } );
947 C4
::Circulation
::AddIssue
( $patron, $item1->{barcode
} );
948 my $first_checkout_tt_letter = Koha
::Notice
::Messages
->search( {}, { order_by
=> { -desc
=> 'message_id' } } )->next;
950 # Change branch name for second checkout notice
951 $library_object->branchname($new_branchname);
952 $library_object->store();
954 C4
::Circulation
::AddIssue
( $patron, $item2->{barcode
} );
955 my $second_checkout_tt_letter = Koha
::Notice
::Messages
->search( {}, { order_by
=> { -desc
=> 'message_id' } } )->next;
957 # Restore old name for first checkin notice
958 $library_object->branchname( $old_branchname );
959 $library_object->store();
961 AddReturn
( $item1->{barcode
} );
962 my $first_checkin_tt_letter = Koha
::Notice
::Messages
->search( {}, { order_by
=> { -desc
=> 'message_id' } } )->next;
964 # Change branch name for second checkin notice
965 $library_object->branchname($new_branchname);
966 $library_object->store();
968 AddReturn
( $item2->{barcode
} );
969 my $second_checkin_tt_letter = Koha
::Notice
::Messages
->search( {}, { order_by
=> { -desc
=> 'message_id' } } )->next;
971 my $first_letter = qq[
974 my $second_letter = qq[
979 is
( $first_checkout_letter->content, $first_letter, 'Verify first checkout letter' );
980 is
( $second_checkout_letter->content, $second_letter, 'Verify second checkout letter' );
981 is
( $first_checkin_letter->content, $first_letter, 'Verify first checkin letter' );
982 is
( $second_checkin_letter->content, $second_letter, 'Verify second checkin letter' );
984 is
( $first_checkout_tt_letter->content, $first_letter, 'Verify TT first checkout letter' );
985 is
( $second_checkout_tt_letter->content, $second_letter, 'Verify TT second checkout letter' );
986 is
( $first_checkin_tt_letter->content, $first_letter, 'Verify TT first checkin letter' );
987 is
( $second_checkin_tt_letter->content, $second_letter, 'Verify TT second checkin letter' );
992 subtest
'loops' => sub {
997 subtest
'primary key is AI' => sub {
999 my $patron_1 = $builder->build({ source
=> 'Borrower' });
1000 my $patron_2 = $builder->build({ source
=> 'Borrower' });
1002 my $template = q
|[% FOREACH patron IN borrowers
%][% patron
.surname
%][% END %]|;
1003 reset_template
( { template
=> $template, code
=> $code, module
=> $module } );
1004 my $letter = GetPreparedLetter
( module
=> $module, letter_code
=> $code, loops
=> { borrowers
=> [ $patron_1->{borrowernumber
}, $patron_2->{borrowernumber
} ] } );
1005 my $expected_letter = join '', ( $patron_1->{surname
}, $patron_2->{surname
} );
1006 is
( $letter->{content
}, $expected_letter, );
1009 subtest
'foreign key is used' => sub {
1011 my $patron_1 = $builder->build({ source
=> 'Borrower' });
1012 my $patron_2 = $builder->build({ source
=> 'Borrower' });
1013 my $checkout_1 = $builder->build({ source
=> 'Issue', value
=> { borrowernumber
=> $patron_1->{borrowernumber
} } } );
1014 my $checkout_2 = $builder->build({ source
=> 'Issue', value
=> { borrowernumber
=> $patron_1->{borrowernumber
} } } );
1016 my $template = q
|[% FOREACH checkout IN checkouts
%][% checkout
.issue_id
%][% END %]|;
1017 reset_template
( { template
=> $template, code
=> $code, module
=> $module } );
1018 my $letter = GetPreparedLetter
( module
=> $module, letter_code
=> $code, loops
=> { issues
=> [ $checkout_1->{itemnumber
}, $checkout_2->{itemnumber
} ] } );
1019 my $expected_letter = join '', ( $checkout_1->{issue_id
}, $checkout_2->{issue_id
} );
1020 is
( $letter->{content
}, $expected_letter, );
1024 subtest
'add_tt_filters' => sub {
1027 my $module = "TEST";
1029 my $patron = $builder->build_object(
1031 class => 'Koha::Patrons',
1032 value
=> { surname
=> "with_punctuation_" }
1035 my $biblio = $builder->build_object(
1036 { class => 'Koha::Biblios', value
=> { title
=> "with_punctuation_" } }
1038 my $biblioitem = $builder->build_object(
1040 class => 'Koha::Biblioitems',
1042 biblionumber
=> $biblio->biblionumber,
1043 isbn
=> "with_punctuation_"
1048 my $template = q
|patron
=[% borrower
.surname
%];biblio
=[% biblio
.title
%];biblioitems
=[% biblioitem
.isbn
%]|;
1049 reset_template
( { template
=> $template, code
=> $code, module
=> $module } );
1050 my $letter = GetPreparedLetter
(
1052 letter_code
=> $code,
1054 borrowers
=> $patron->borrowernumber,
1055 biblio
=> $biblio->biblionumber,
1056 biblioitems
=> $biblioitem->biblioitemnumber
1059 my $expected_letter = q
|patron
=with_punctuation_
;biblio
=with_punctuation
;biblioitems
=with_punctuation
|;
1060 is
( $letter->{content
}, $expected_letter, "Pre-processing should call TT plugin to remove punctuation if table is biblio or biblioitems");
1063 subtest
'Dates formatting' => sub {
1065 my $code = 'TEST_DATE';
1066 t
::lib
::Mocks
::mock_preference
('dateformat', 'metric'); # MM/DD/YYYY
1067 my $biblio = $builder->build_object(
1069 class => 'Koha::Biblios',
1071 timestamp
=> '2018-12-13 20:21:22',
1072 datecreated
=> '2018-12-13'
1076 my $template = <<EOF;
1077 [%- USE KohaDates -%]
1078 [% biblio.timestamp %]
1079 [% biblio.timestamp | \$KohaDates %]
1080 [% biblio.timestamp | \$KohaDates with_hours => 1 %]
1082 [% biblio.datecreated %]
1083 [% biblio.datecreated | \$KohaDates %]
1084 [% biblio.datecreated | \$KohaDates with_hours => 1 %]
1086 [% biblio.timestamp | \$KohaDates dateformat => 'iso' %]
1087 [% KohaDates.output_preference( str => biblio.timestamp, dateformat => 'iso' ) %]
1088 [% KohaDates.output_preference( str => biblio.timestamp, dateformat => 'iso', dateonly => 1 ) %]
1090 reset_template
({ template
=> $template, code
=> $code, module
=> 'test' });
1091 my $letter = GetPreparedLetter
(
1093 letter_code
=> $code,
1095 biblio
=> $biblio->biblionumber,
1098 my $expected_content = sprintf("%s\n%s\n%s\n\n%s\n%s\n%s\n\n%s\n%s\n%s\n",
1099 '2018-12-13 20:21:22',
1111 is
( $letter->{content
}, $expected_content );
1114 sub reset_template
{
1115 my ( $params ) = @_;
1116 my $template = $params->{template
};
1117 my $code = $params->{code
};
1118 my $module = $params->{module
} || 'test_module';
1120 Koha
::Notice
::Templates
->search( { code
=> $code } )->delete;
1121 Koha
::Notice
::Template
->new(
1128 message_transport_type
=> 'email',
1129 content
=> $template
1134 sub process_letter
{
1136 my $template = $params->{template
};
1137 my $tables = $params->{tables
};
1138 my $substitute = $params->{substitute
};
1139 my $code = $params->{code
};
1140 my $module = $params->{module
} || 'test_module';
1141 my $branchcode = $params->{branchcode
};
1143 reset_template
( $params );
1145 my $letter = C4
::Letters
::GetPreparedLetter
(
1147 letter_code
=> $code,
1150 substitute
=> $substitute,