Bug 18433: Remember item search results selected rows in session storage
[koha.git] / t / db_dependent / Letters / TemplateToolkit.t
blob80415d62cad2daf8e4b1ef98bbe47e2801b17c3b
1 #!/usr/bin/perl
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>.
21 use Modern::Perl;
22 use Test::More tests => 19;
23 use Test::MockModule;
24 use Test::Warn;
26 use MARC::Record;
28 use t::lib::TestBuilder;
29 use t::lib::Mocks;
31 use C4::Circulation;
32 use C4::Letters;
33 use C4::Members;
34 use C4::Biblio;
35 use Koha::Database;
36 use Koha::DateUtils;
37 use Koha::ArticleRequests;
38 use Koha::Biblio;
39 use Koha::Biblioitem;
40 use Koha::Item;
41 use Koha::Hold;
42 use Koha::NewsItem;
43 use Koha::Serial;
44 use Koha::Subscription;
45 use Koha::Suggestion;
46 use Koha::Checkout;
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'
73 )->store();
75 my $biblioitem = Koha::Biblioitem->new(
77 biblionumber => $biblio->id()
79 )->store();
81 my $item = Koha::Item->new(
83 biblionumber => $biblio->id(),
84 biblioitemnumber => $biblioitem->id()
86 )->store();
88 my $hold = Koha::Hold->new(
90 borrowernumber => $patron->{borrowernumber},
91 biblionumber => $biblio->id()
93 )->store();
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();
102 my $prepared_letter;
104 my $sth =
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(
110 module => 'test',
111 letter_code => 'TEST_PATRON',
112 tables => {
113 borrowers => $patron->{borrowernumber},
117 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with scalar' );
119 $prepared_letter = GetPreparedLetter(
121 module => 'test',
122 letter_code => 'TEST_PATRON',
123 tables => {
124 borrowers => $patron,
128 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with hashref' );
130 $prepared_letter = GetPreparedLetter(
132 module => 'test',
133 letter_code => 'TEST_PATRON',
134 tables => {
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(
144 module => 'test',
145 letter_code => 'TEST_BIBLIO',
146 tables => {
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(
156 module => 'test',
157 letter_code => 'TEST_LIBRARY',
158 tables => {
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(
168 module => 'test',
169 letter_code => 'TEST_ITEM',
170 tables => {
171 items => $item->id()
175 is( $prepared_letter->{content}, $item->id(), 'Item object used correctly' );
177 $sth->execute( "TEST_NEWS", "[% news.id %]" );
178 $prepared_letter = GetPreparedLetter(
180 module => 'test',
181 letter_code => 'TEST_NEWS',
182 tables => {
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(
192 module => 'test',
193 letter_code => 'TEST_HOLD',
194 tables => {
195 reserves => { borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio->id() },
199 is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly' );
201 eval {
202 $prepared_letter = GetPreparedLetter(
204 module => 'test',
205 letter_code => 'TEST_HOLD',
206 tables => {
207 reserves => [ $patron->{borrowernumber}, $biblio->id() ],
212 my $croak = $@;
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" );
215 # Bug 16942
216 $prepared_letter = GetPreparedLetter(
218 module => 'test',
219 letter_code => 'TEST_HOLD',
220 tables => {
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(
235 module => 'test',
236 letter_code => 'TEST_SERIAL',
237 tables => {
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(
247 module => 'test',
248 letter_code => 'TEST_SUBSCRIPTION',
249 tables => {
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(
259 module => 'test',
260 letter_code => 'TEST_SUGGESTION',
261 tables => {
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(
271 module => 'test',
272 letter_code => 'TEST_ISSUE',
273 tables => {
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(
283 module => 'test',
284 letter_code => 'TEST_MODIFICATION',
285 tables => {
286 borrower_modifications => $modification->verification_token,
290 is( $prepared_letter->{content}, $modification->id(), 'Patron modification object used correctly' );
292 subtest 'regression tests' => sub {
293 plan tests => 8;
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},
306 itype => 'BK',
307 itemcallnumber => 'itemcallnumber1',
309 )->store->unblessed;
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},
319 itype => 'BK',
320 itemcallnumber => 'itemcallnumber2',
322 )->store->unblessed;
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},
332 itype => 'BK',
333 itemcallnumber => 'itemcallnumber3',
335 )->store->unblessed;
337 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
339 subtest 'ACQ_NOTIF_ON_RECEIV ' => sub {
340 plan tests => 1;
341 my $code = 'ACQ_NOTIF_ON_RECEIV';
342 my $branchcode = $library->{branchcode};
343 my $order = $builder->build({ source => 'Aqorder' });
345 my $template = q|
346 Dear <<borrowers.firstname>> <<borrowers.surname>>,
347 The order <<aqorders.ordernumber>> (<<biblio.title>>) has been received.
348 Your library.
350 my $params = { code => $code, branchcode => $branchcode, tables => { branches => $library, borrowers => $patron, biblio => $biblio1, aqorders => $order } };
351 my $letter = process_letter( { template => $template, %$params });
352 my $tt_template = q|
353 Dear [% borrower.firstname %] [% borrower.surname %],
354 The order [% order.ordernumber %] ([% biblio.title %]) has been received.
355 Your library.
357 my $tt_letter = process_letter( { template => $tt_template, %$params });
359 is( $tt_letter->{content}, $letter->{content}, 'Verified letter content' );
362 subtest 'AR_*' => sub {
363 plan tests => 2;
364 my $code = 'AR_CANCELED';
365 my $branchcode = $library->{branchcode};
367 my $template = q|
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>>
374 Article requested:
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;
389 my $tt_template = q|
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 %]
396 Article requested:
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 {
414 plan tests => 4;
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' );
429 # historic syntax
430 my $checkout_template = q|
431 The following items have been checked out:
432 ----
433 <<biblio.title>>
434 ----
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:
440 ----
441 <<biblio.title>> was due on <<old_issues.date_due | dateonly>>
442 ----
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;
459 # TT syntax
460 $checkout_template = q|
461 The following items have been checked out:
462 ----
463 [% biblio.title %]
464 ----
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:
470 ----
471 [% biblio.title %] was due on [% old_checkout.date_due | $KohaDates %]
472 ----
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 {
495 plan tests => 1;
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' );
505 my $params = {
506 code => $code,
507 substitute => { count => 42 },
510 my $template = q|
511 You have <<count>> items due
513 my $letter = process_letter( { template => $template, %$params });
515 my $tt_template = q|
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 {
523 plan tests => 2;
525 my $code = 'HOLD_SLIP';
527 C4::Reserves::AddReserve(
529 branchcode => $library->{branchcode},
530 borrowernumber => $patron->{borrowernumber},
531 biblionumber => $biblio1->{biblionumber},
532 notes => "a note",
533 itemnumber => $item1->{itemnumber},
534 found => 'W'
537 C4::Reserves::AddReserve(
539 branchcode => $library->{branchcode},
540 borrowernumber => $patron->{borrowernumber},
541 biblionumber => $biblio2->{biblionumber},
542 notes => "another note",
543 itemnumber => $item2->{itemnumber},
547 my $template = <<EOF;
548 <h5>Date: <<today>></h5>
550 <h3> Transfer to/Hold in <<branches.branchname>></h3>
552 <h3><<borrowers.surname>>, <<borrowers.firstname>></h3>
554 <ul>
555 <li><<borrowers.cardnumber>></li>
556 <li><<borrowers.phone>></li>
557 <li> <<borrowers.address>><br />
558 <<borrowers.address2>><br />
559 <<borrowers.city>> <<borrowers.zipcode>>
560 </li>
561 <li><<borrowers.email>></li>
562 </ul>
563 <br />
564 <h3>ITEM ON HOLD</h3>
565 <h4><<biblio.title>></h4>
566 <h5><<biblio.author>></h5>
567 <ul>
568 <li><<items.barcode>></li>
569 <li><<items.itemcallnumber>></li>
570 <li><<reserves.waitingdate>></li>
571 </ul>
572 <p>Notes:
573 <pre><<reserves.reservenotes>></pre>
574 </p>
577 reset_template( { template => $template, code => $code, module => 'circulation' } );
578 my $letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio1->{biblionumber} } );
579 my $letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio2->{biblionumber} } );
581 my $tt_template = <<EOF;
582 <h5>Date: [% today | \$KohaDates with_hours => 1 %]</h5>
584 <h3> Transfer to/Hold in [% branch.branchname %]</h3>
586 <h3>[% borrower.surname %], [% borrower.firstname %]</h3>
588 <ul>
589 <li>[% borrower.cardnumber %]</li>
590 <li>[% borrower.phone %]</li>
591 <li> [% borrower.address %]<br />
592 [% borrower.address2 %]<br />
593 [% borrower.city %] [% borrower.zipcode %]
594 </li>
595 <li>[% borrower.email %]</li>
596 </ul>
597 <br />
598 <h3>ITEM ON HOLD</h3>
599 <h4>[% biblio.title %]</h4>
600 <h5>[% biblio.author %]</h5>
601 <ul>
602 <li>[% item.barcode %]</li>
603 <li>[% item.itemcallnumber %]</li>
604 <li>[% hold.waitingdate | \$KohaDates %]</li>
605 </ul>
606 <p>Notes:
607 <pre>[% hold.reservenotes %]</pre>
608 </p>
611 reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
612 my $tt_letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio1->{biblionumber} } );
613 my $tt_letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio2->{biblionumber} } );
615 is( $tt_letter_for_item1->{content}, $letter_for_item1->{content}, );
616 is( $tt_letter_for_item2->{content}, $letter_for_item2->{content}, );
619 subtest 'ISSUESLIP|checkedout|repeat' => sub {
620 plan tests => 2;
622 my $code = 'ISSUESLIP';
623 my $now = dt_from_string;
624 my $one_minute_ago = dt_from_string->subtract( minutes => 1 );
626 my $branchcode = $library->{branchcode};
628 Koha::News->delete;
629 my $news_item = Koha::NewsItem->new({ branchcode => $branchcode, title => "A wonderful news", content => "This is the wonderful news." })->store;
631 # historic syntax
632 my $template = <<EOF;
633 <h3><<branches.branchname>></h3>
634 Checked out to <<borrowers.title>> <<borrowers.firstname>> <<borrowers.initials>> <<borrowers.surname>> <br />
635 (<<borrowers.cardnumber>>) <br />
637 <<today>><br />
639 <h4>Checked Out</h4>
640 <checkedout>
642 <<biblio.title>> <br />
643 Barcode: <<items.barcode>><br />
644 Date due: <<issues.date_due | dateonly>><br />
645 </p>
646 </checkedout>
648 <h4>Overdues</h4>
649 <overdue>
651 <<biblio.title>> <br />
652 Barcode: <<items.barcode>><br />
653 Date due: <<issues.date_due | dateonly>><br />
654 </p>
655 </overdue>
657 <hr>
659 <h4 style="text-align: center; font-style:italic;">News</h4>
660 <news>
661 <div class="newsitem">
662 <h5 style="margin-bottom: 1px; margin-top: 1px"><b><<opac_news.title>></b></h5>
663 <p style="margin-bottom: 1px; margin-top: 1px"><<opac_news.content>></p>
664 <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on <<opac_news.timestamp>></p>
665 <hr />
666 </div>
667 </news>
670 reset_template( { template => $template, code => $code, module => 'circulation' } );
672 my $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
673 $checkout->set( { timestamp => $now, issuedate => $one_minute_ago } )->store;
674 my $first_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
676 $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
677 $checkout->set( { timestamp => $now, issuedate => $now } )->store;
678 my $yesterday = dt_from_string->subtract( days => 1 );
679 C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
680 my $second_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
682 # Cleanup
683 AddReturn( $item1->{barcode} );
684 AddReturn( $item2->{barcode} );
685 AddReturn( $item3->{barcode} );
687 # TT syntax
688 my $tt_template = <<EOF;
689 <h3>[% branch.branchname %]</h3>
690 Checked out to [% borrower.title %] [% borrower.firstname %] [% borrower.initials %] [% borrower.surname %] <br />
691 ([% borrower.cardnumber %]) <br />
693 [% today | \$KohaDates with_hours => 1 %]<br />
695 <h4>Checked Out</h4>
696 [% FOREACH checkout IN checkouts %]
697 [%~ SET item = checkout.item %]
698 [%~ SET biblio = checkout.item.biblio %]
700 [% biblio.title %] <br />
701 Barcode: [% item.barcode %]<br />
702 Date due: [% checkout.date_due | \$KohaDates %]<br />
703 </p>
704 [% END %]
706 <h4>Overdues</h4>
707 [% FOREACH overdue IN overdues %]
708 [%~ SET item = overdue.item %]
709 [%~ SET biblio = overdue.item.biblio %]
711 [% biblio.title %] <br />
712 Barcode: [% item.barcode %]<br />
713 Date due: [% overdue.date_due | \$KohaDates %]<br />
714 </p>
715 [% END %]
717 <hr>
719 <h4 style="text-align: center; font-style:italic;">News</h4>
720 [% FOREACH n IN news %]
721 <div class="newsitem">
722 <h5 style="margin-bottom: 1px; margin-top: 1px"><b>[% n.title %]</b></h5>
723 <p style="margin-bottom: 1px; margin-top: 1px">[% n.content %]</p>
724 <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on [% n.timestamp | \$KohaDates %]</p>
725 <hr />
726 </div>
727 [% END %]
730 reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
732 $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
733 $checkout->set( { timestamp => $now, issuedate => $one_minute_ago } )->store;
734 my $first_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
736 $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
737 $checkout->set( { timestamp => $now, issuedate => $now } )->store;
738 C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
739 my $second_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
741 # There is too many line breaks generated by the historic syntax
742 $second_slip->{content} =~ s|</p>\n\n\n<p>|</p>\n\n<p>|s;
744 is( $first_tt_slip->{content}, $first_slip->{content}, );
745 is( $second_tt_slip->{content}, $second_slip->{content}, );
747 # Cleanup
748 AddReturn( $item1->{barcode} );
749 AddReturn( $item2->{barcode} );
750 AddReturn( $item3->{barcode} );
753 subtest 'ODUE|items.content|item' => sub {
754 plan tests => 1;
756 my $code = 'ODUE';
758 my $branchcode = $library->{branchcode};
760 # historic syntax
761 # FIXME items.fine does not work with TT notices
762 # See bug 17976
763 # <item> should contain Fine: <<items.fine>></item>
764 my $template = <<EOF;
765 Dear <<borrowers.firstname>> <<borrowers.surname>>,
767 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.
769 <<branches.branchname>>
770 <<branches.branchaddress1>>
771 <<branches.branchaddress2>> <<branches.branchaddress3>>
772 Phone: <<branches.branchphone>>
773 Fax: <<branches.branchfax>>
774 Email: <<branches.branchemail>>
776 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.
778 The following item(s) is/are currently overdue:
780 <item>"<<biblio.title>>" by <<biblio.author>>, <<items.itemcallnumber>>, Barcode: <<items.barcode>></item>
782 <<items.content>>
784 Thank-you for your prompt attention to this matter.
786 <<branches.branchname>> Staff
789 reset_template( { template => $template, code => $code, module => 'circulation' } );
791 my $yesterday = dt_from_string->subtract( days => 1 );
792 my $two_days_ago = dt_from_string->subtract( days => 2 );
793 my $issue1 = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
794 my $issue2 = C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
795 my $issue3 = C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
796 $issue1 = $issue1->unblessed;
797 $issue2 = $issue2->unblessed;
798 $issue3 = $issue3->unblessed;
800 # For items.content
801 my @item_fields = qw( date_due title barcode author itemnumber );
802 my $items_content = C4::Letters::get_item_content( { item => { %$item1, %$biblio1, %$issue1 }, item_content_fields => \@item_fields, dateonly => 1 } );
803 $items_content .= C4::Letters::get_item_content( { item => { %$item2, %$biblio2, %$issue2 }, item_content_fields => \@item_fields, dateonly => 1 } );
804 $items_content .= C4::Letters::get_item_content( { item => { %$item3, %$biblio3, %$issue3 }, item_content_fields => \@item_fields, dateonly => 1 } );
806 my @items = ( $item1, $item2, $item3 );
807 my $letter = C4::Overdues::parse_overdues_letter(
809 letter_code => $code,
810 borrowernumber => $patron->{borrowernumber},
811 branchcode => $library->{branchcode},
812 items => \@items,
813 substitute => {
814 bib => $library->{branchname},
815 'items.content' => $items_content,
816 count => scalar( @items ),
817 message_transport_type => 'email',
822 # Cleanup
823 AddReturn( $item1->{barcode} );
824 AddReturn( $item2->{barcode} );
825 AddReturn( $item3->{barcode} );
828 # historic syntax
829 my $tt_template = <<EOF;
830 Dear [% borrower.firstname %] [% borrower.surname %],
832 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.
834 [% branch.branchname %]
835 [% branch.branchaddress1 %]
836 [% branch.branchaddress2 %] [% branch.branchaddress3 %]
837 Phone: [% branch.branchphone %]
838 Fax: [% branch.branchfax %]
839 Email: [% branch.branchemail %]
841 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.
843 The following item(s) is/are currently overdue:
845 [% FOREACH overdue IN overdues %]
846 [%~ SET item = overdue.item ~%]
847 "[% item.biblio.title %]" by [% item.biblio.author %], [% item.itemcallnumber %], Barcode: [% item.barcode %]
848 [% END %]
849 [% FOREACH overdue IN overdues %]
850 [%~ SET item = overdue.item ~%]
851 [% overdue.date_due | \$KohaDates %]\t[% item.biblio.title %]\t[% item.barcode %]\t[% item.biblio.author %]\t[% item.itemnumber %]
852 [% END %]
854 Thank-you for your prompt attention to this matter.
856 [% branch.branchname %] Staff
859 reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
861 C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
862 C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
863 C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
865 my $tt_letter = C4::Overdues::parse_overdues_letter(
867 letter_code => $code,
868 borrowernumber => $patron->{borrowernumber},
869 branchcode => $library->{branchcode},
870 items => \@items,
871 substitute => {
872 bib => $library->{branchname},
873 'items.content' => $items_content,
874 count => scalar( @items ),
875 message_transport_type => 'email',
880 is( $tt_letter->{content}, $letter->{content}, );
883 subtest 'Bug 19743 - Header and Footer should be updated on each item for checkin / checkout / renewal notices' => sub {
884 plan tests => 8;
886 my $checkout_code = 'CHECKOUT';
887 my $checkin_code = 'CHECKIN';
889 my $dbh = C4::Context->dbh;
890 $dbh->do("DELETE FROM letter");
891 $dbh->do("DELETE FROM issues");
892 $dbh->do("DELETE FROM message_queue");
894 # Enable notification for CHECKOUT - Things are hardcoded here but should work with default data
895 $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 6 );
896 my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
897 $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
898 # Enable notification for CHECKIN - Things are hardcoded here but should work with default data
899 $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 5 );
900 $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
901 $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
903 my $checkout_template = q|
904 <<branches.branchname>>
905 ----
906 ----
908 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
909 my $checkin_template = q[
910 <<branches.branchname>>
911 ----
912 ----
914 reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
916 my $issue = C4::Circulation::AddIssue( $patron, $item1->{barcode} );
917 my $first_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
919 my $library_object = Koha::Libraries->find( $issue->branchcode );
920 my $old_branchname = $library_object->branchname;
921 my $new_branchname = "Kyle M Hall Memorial Library";
923 # Change branch name for second checkout notice
924 $library_object->branchname($new_branchname);
925 $library_object->store();
927 C4::Circulation::AddIssue( $patron, $item2->{barcode} );
928 my $second_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
930 # Restore old name for first checkin notice
931 $library_object->branchname( $old_branchname );
932 $library_object->store();
934 AddReturn( $item1->{barcode} );
935 my $first_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
937 # Change branch name for second checkin notice
938 $library_object->branchname($new_branchname);
939 $library_object->store();
941 AddReturn( $item2->{barcode} );
942 my $second_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
944 # Restore old name for first TT checkout notice
945 $library_object->branchname( $old_branchname );
946 $library_object->store();
948 Koha::Notice::Messages->delete;
950 # TT syntax
951 $checkout_template = q|
952 [% branch.branchname %]
953 ----
954 ----
956 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
957 $checkin_template = q[
958 [% branch.branchname %]
959 ----
960 ----
962 reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
964 C4::Circulation::AddIssue( $patron, $item1->{barcode} );
965 my $first_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
967 # Change branch name for second checkout notice
968 $library_object->branchname($new_branchname);
969 $library_object->store();
971 C4::Circulation::AddIssue( $patron, $item2->{barcode} );
972 my $second_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
974 # Restore old name for first checkin notice
975 $library_object->branchname( $old_branchname );
976 $library_object->store();
978 AddReturn( $item1->{barcode} );
979 my $first_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
981 # Change branch name for second checkin notice
982 $library_object->branchname($new_branchname);
983 $library_object->store();
985 AddReturn( $item2->{barcode} );
986 my $second_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
988 my $first_letter = qq[
989 $old_branchname
991 my $second_letter = qq[
992 $new_branchname
996 is( $first_checkout_letter->content, $first_letter, 'Verify first checkout letter' );
997 is( $second_checkout_letter->content, $second_letter, 'Verify second checkout letter' );
998 is( $first_checkin_letter->content, $first_letter, 'Verify first checkin letter' );
999 is( $second_checkin_letter->content, $second_letter, 'Verify second checkin letter' );
1001 is( $first_checkout_tt_letter->content, $first_letter, 'Verify TT first checkout letter' );
1002 is( $second_checkout_tt_letter->content, $second_letter, 'Verify TT second checkout letter' );
1003 is( $first_checkin_tt_letter->content, $first_letter, 'Verify TT first checkin letter' );
1004 is( $second_checkin_tt_letter->content, $second_letter, 'Verify TT second checkin letter' );
1009 subtest 'loops' => sub {
1010 plan tests => 2;
1011 my $code = "TEST";
1012 my $module = "TEST";
1014 subtest 'primary key is AI' => sub {
1015 plan tests => 1;
1016 my $patron_1 = $builder->build({ source => 'Borrower' });
1017 my $patron_2 = $builder->build({ source => 'Borrower' });
1019 my $template = q|[% FOREACH patron IN borrowers %][% patron.surname %][% END %]|;
1020 reset_template( { template => $template, code => $code, module => $module } );
1021 my $letter = GetPreparedLetter( module => $module, letter_code => $code, loops => { borrowers => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber} ] } );
1022 my $expected_letter = join '', ( $patron_1->{surname}, $patron_2->{surname} );
1023 is( $letter->{content}, $expected_letter, );
1026 subtest 'foreign key is used' => sub {
1027 plan tests => 1;
1028 my $patron_1 = $builder->build({ source => 'Borrower' });
1029 my $patron_2 = $builder->build({ source => 'Borrower' });
1030 my $checkout_1 = $builder->build({ source => 'Issue', value => { borrowernumber => $patron_1->{borrowernumber} } } );
1031 my $checkout_2 = $builder->build({ source => 'Issue', value => { borrowernumber => $patron_1->{borrowernumber} } } );
1033 my $template = q|[% FOREACH checkout IN checkouts %][% checkout.issue_id %][% END %]|;
1034 reset_template( { template => $template, code => $code, module => $module } );
1035 my $letter = GetPreparedLetter( module => $module, letter_code => $code, loops => { issues => [ $checkout_1->{itemnumber}, $checkout_2->{itemnumber} ] } );
1036 my $expected_letter = join '', ( $checkout_1->{issue_id}, $checkout_2->{issue_id} );
1037 is( $letter->{content}, $expected_letter, );
1041 subtest 'add_tt_filters' => sub {
1042 plan tests => 1;
1043 my $code = "TEST";
1044 my $module = "TEST";
1046 my $patron = $builder->build_object(
1048 class => 'Koha::Patrons',
1049 value => { surname => "with_punctuation_" }
1052 my $biblio = $builder->build_object(
1053 { class => 'Koha::Biblios', value => { title => "with_punctuation_" } }
1055 my $biblioitem = $builder->build_object(
1057 class => 'Koha::Biblioitems',
1058 value => {
1059 biblionumber => $biblio->biblionumber,
1060 isbn => "with_punctuation_"
1065 my $template = q|patron=[% borrower.surname %];biblio=[% biblio.title %];biblioitems=[% biblioitem.isbn %]|;
1066 reset_template( { template => $template, code => $code, module => $module } );
1067 my $letter = GetPreparedLetter(
1068 module => $module,
1069 letter_code => $code,
1070 tables => {
1071 borrowers => $patron->borrowernumber,
1072 biblio => $biblio->biblionumber,
1073 biblioitems => $biblioitem->biblioitemnumber
1076 my $expected_letter = q|patron=with_punctuation_;biblio=with_punctuation;biblioitems=with_punctuation|;
1077 is( $letter->{content}, $expected_letter, "Pre-processing should call TT plugin to remove punctuation if table is biblio or biblioitems");
1080 subtest 'Dates formatting' => sub {
1081 plan tests => 1;
1082 my $code = 'TEST_DATE';
1083 t::lib::Mocks::mock_preference('dateformat', 'metric'); # MM/DD/YYYY
1084 my $biblio = $builder->build_object(
1086 class => 'Koha::Biblios',
1087 value => {
1088 timestamp => '2018-12-13 20:21:22',
1089 datecreated => '2018-12-13'
1093 my $template = <<EOF;
1094 [%- USE KohaDates -%]
1095 [% biblio.timestamp %]
1096 [% biblio.timestamp | \$KohaDates %]
1097 [% biblio.timestamp | \$KohaDates with_hours => 1 %]
1099 [% biblio.datecreated %]
1100 [% biblio.datecreated | \$KohaDates %]
1101 [% biblio.datecreated | \$KohaDates with_hours => 1 %]
1103 [% biblio.timestamp | \$KohaDates dateformat => 'iso' %]
1104 [% KohaDates.output_preference( str => biblio.timestamp, dateformat => 'iso' ) %]
1105 [% KohaDates.output_preference( str => biblio.timestamp, dateformat => 'iso', dateonly => 1 ) %]
1107 reset_template({ template => $template, code => $code, module => 'test' });
1108 my $letter = GetPreparedLetter(
1109 module => 'test',
1110 letter_code => $code,
1111 tables => {
1112 biblio => $biblio->biblionumber,
1115 my $expected_content = sprintf("%s\n%s\n%s\n\n%s\n%s\n%s\n\n%s\n%s\n%s\n",
1116 '2018-12-13 20:21:22',
1117 '13/12/2018',
1118 '13/12/2018 20:21',
1120 '2018-12-13',
1121 '13/12/2018',
1122 '13/12/2018 00:00',
1124 '2018-12-13',
1125 '2018-12-13 20:21',
1126 '2018-12-13',
1128 is( $letter->{content}, $expected_content );
1131 sub reset_template {
1132 my ( $params ) = @_;
1133 my $template = $params->{template};
1134 my $code = $params->{code};
1135 my $module = $params->{module} || 'test_module';
1137 Koha::Notice::Templates->search( { code => $code } )->delete;
1138 Koha::Notice::Template->new(
1140 module => $module,
1141 code => $code,
1142 branchcode => '',
1143 name => $code,
1144 title => $code,
1145 message_transport_type => 'email',
1146 content => $template
1148 )->store;
1151 sub process_letter {
1152 my ($params) = @_;
1153 my $template = $params->{template};
1154 my $tables = $params->{tables};
1155 my $substitute = $params->{substitute};
1156 my $code = $params->{code};
1157 my $module = $params->{module} || 'test_module';
1158 my $branchcode = $params->{branchcode};
1160 reset_template( $params );
1162 my $letter = C4::Letters::GetPreparedLetter(
1163 module => $module,
1164 letter_code => $code,
1165 branchcode => '',
1166 tables => $tables,
1167 substitute => $substitute,
1169 return $letter;