Bug 21824: Add tests
[koha.git] / t / db_dependent / Letters / TemplateToolkit.t
blobfff90b17fb5cfa8ccee0f8116ac08253a4a9a35c
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( $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>
537 <ul>
538 <li><<borrowers.cardnumber>></li>
539 <li><<borrowers.phone>></li>
540 <li> <<borrowers.address>><br />
541 <<borrowers.address2>><br />
542 <<borrowers.city>> <<borrowers.zipcode>>
543 </li>
544 <li><<borrowers.email>></li>
545 </ul>
546 <br />
547 <h3>ITEM ON HOLD</h3>
548 <h4><<biblio.title>></h4>
549 <h5><<biblio.author>></h5>
550 <ul>
551 <li><<items.barcode>></li>
552 <li><<items.itemcallnumber>></li>
553 <li><<reserves.waitingdate>></li>
554 </ul>
555 <p>Notes:
556 <pre><<reserves.reservenotes>></pre>
557 </p>
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>
571 <ul>
572 <li>[% borrower.cardnumber %]</li>
573 <li>[% borrower.phone %]</li>
574 <li> [% borrower.address %]<br />
575 [% borrower.address2 %]<br />
576 [% borrower.city %] [% borrower.zipcode %]
577 </li>
578 <li>[% borrower.email %]</li>
579 </ul>
580 <br />
581 <h3>ITEM ON HOLD</h3>
582 <h4>[% biblio.title %]</h4>
583 <h5>[% biblio.author %]</h5>
584 <ul>
585 <li>[% item.barcode %]</li>
586 <li>[% item.itemcallnumber %]</li>
587 <li>[% hold.waitingdate | \$KohaDates %]</li>
588 </ul>
589 <p>Notes:
590 <pre>[% hold.reservenotes %]</pre>
591 </p>
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 {
603 plan tests => 2;
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};
611 Koha::News->delete;
612 my $news_item = Koha::NewsItem->new({ branchcode => $branchcode, title => "A wonderful news", content => "This is the wonderful news." })->store;
614 # historic syntax
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 />
620 <<today>><br />
622 <h4>Checked Out</h4>
623 <checkedout>
625 <<biblio.title>> <br />
626 Barcode: <<items.barcode>><br />
627 Date due: <<issues.date_due | dateonly>><br />
628 </p>
629 </checkedout>
631 <h4>Overdues</h4>
632 <overdue>
634 <<biblio.title>> <br />
635 Barcode: <<items.barcode>><br />
636 Date due: <<issues.date_due | dateonly>><br />
637 </p>
638 </overdue>
640 <hr>
642 <h4 style="text-align: center; font-style:italic;">News</h4>
643 <news>
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>
648 <hr />
649 </div>
650 </news>
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} );
665 # Cleanup
666 AddReturn( $item1->{barcode} );
667 AddReturn( $item2->{barcode} );
668 AddReturn( $item3->{barcode} );
670 # TT syntax
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 />
678 <h4>Checked Out</h4>
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 />
686 </p>
687 [% END %]
689 <h4>Overdues</h4>
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 />
697 </p>
698 [% END %]
700 <hr>
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>
708 <hr />
709 </div>
710 [% END %]
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}, );
730 # Cleanup
731 AddReturn( $item1->{barcode} );
732 AddReturn( $item2->{barcode} );
733 AddReturn( $item3->{barcode} );
736 subtest 'ODUE|items.content|item' => sub {
737 plan tests => 1;
739 my $code = 'ODUE';
741 my $branchcode = $library->{branchcode};
743 # historic syntax
744 # FIXME items.fine does not work with TT notices
745 # See bug 17976
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>
765 <<items.content>>
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;
783 # For items.content
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},
795 items => \@items,
796 substitute => {
797 bib => $library->{branchname},
798 'items.content' => $items_content,
799 count => scalar( @items ),
800 message_transport_type => 'email',
805 # Cleanup
806 AddReturn( $item1->{barcode} );
807 AddReturn( $item2->{barcode} );
808 AddReturn( $item3->{barcode} );
811 # historic syntax
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 %]
831 [% END %]
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 %]
835 [% END %]
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},
853 items => \@items,
854 substitute => {
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 {
867 plan tests => 8;
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>>
888 ----
889 ----
891 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
892 my $checkin_template = q[
893 <<branches.branchname>>
894 ----
895 ----
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;
933 # TT syntax
934 $checkout_template = q|
935 [% branch.branchname %]
936 ----
937 ----
939 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
940 $checkin_template = q[
941 [% branch.branchname %]
942 ----
943 ----
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[
972 $old_branchname
974 my $second_letter = qq[
975 $new_branchname
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 {
993 plan tests => 2;
994 my $code = "TEST";
995 my $module = "TEST";
997 subtest 'primary key is AI' => sub {
998 plan tests => 1;
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 {
1010 plan tests => 1;
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 {
1025 plan tests => 1;
1026 my $code = "TEST";
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',
1041 value => {
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(
1051 module => $module,
1052 letter_code => $code,
1053 tables => {
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 {
1064 plan tests => 1;
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',
1070 value => {
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(
1092 module => 'test',
1093 letter_code => $code,
1094 tables => {
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',
1100 '13/12/2018',
1101 '13/12/2018 20:21',
1103 '2018-12-13',
1104 '13/12/2018',
1105 '13/12/2018 00:00',
1107 '2018-12-13',
1108 '2018-12-13 20:21',
1109 '2018-12-13',
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(
1123 module => $module,
1124 code => $code,
1125 branchcode => '',
1126 name => $code,
1127 title => $code,
1128 message_transport_type => 'email',
1129 content => $template
1131 )->store;
1134 sub process_letter {
1135 my ($params) = @_;
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(
1146 module => $module,
1147 letter_code => $code,
1148 branchcode => '',
1149 tables => $tables,
1150 substitute => $substitute,
1152 return $letter;